Skip to content

Latest commit

 

History

History
625 lines (488 loc) · 32 KB

README.md

File metadata and controls

625 lines (488 loc) · 32 KB

spring-security# All Spring Boot Tutorials:

Spring Boot Basics

Spring Boot REST API Development

Spring Boot Web Application Development

Spring Boot + Angular - Full Stack Development

Spring Boot + ReactJS - Full Stack Development

Spring Boot + Vue JS - Full Stack Development

Spring Boot with Spring Security

Spring Boot + Spring AOP

Spring Boot + Spring Data JPA

Spring Boot Configuration Examples

Spring Boot Testing

Spring and Spring Boot Annotations

Spring Boot + Databases

Spring Boot + Spring JDBC

Spring Boot with JSON

Spring Boot Mini Projects - Real-Time Projects

Spring Boot Hibernate Mapping

Spring Boot + MongoDB

Free Spring Boot Projects

Spring Security Image:

spring-security

NỘI DUNG CẦN NHỚ (NOTE)

Logging in Spring Boot

  • Mức độ log: ERROR, WARN, INFO, DEBUG và TRACE.
  • Các cấu hình mặc định được cung cấp để hỗ trợ cho Java Util Logging, Log4J2, SLF4J và Logback.
Mức độ Màu Nội dung
FATAL Đỏ Chỉ định các sự kiện lỗi rất nghiêm trọng có thể khiến ứng dụng bị hủy bỏ hoặc dừng lại.
ERROR Đỏ Chỉ định các sự kiện lỗi có thể xảy ra mà vẫn cho phép ứng dụng tiếp tục chạy.
WARN Vàng Chỉ định các tình hướng có thể gây hại cho ứng dụng đang chạy.
INFO Xanh Chỉ định các thông báo cung cấp thông tin làm nổi bật tiến trình của ứng dụng ở cấp độ chi tiết.
DEBUG Xanh Chỉ định các sự kiện thông tin chi tiết hữu ích để gỡ lỗi ứng dụng.
TRACE Xanh Chỉ định các sự kiện thông tin chi tiết hơn cấp độ DEBUG.

image

Anotation Cần Note:

  • @Value("${jwt.secret-key}"): lưu ý, @Value là annotation của Spring và không hoạt động với các thuộc tinh Static

  • @SqlResultSetMappings: được sử dụng để định nghĩa các kết quả của câu truy vấn SQL dưới dạng tập hợp các @SqlResultSetMapping annotation. được sử dụng để ánh xạ các cột quan hệ dữ liệu của câu truy vấn SQL thành đối tượng Java. Bằng cách chỉ định tên của @SqlResultSetMapping annotation, bạn có thể lựa chọn các cột từ kết quả truy vấn và ánh xạ chúng vào các thuộc tính của đối tượng Java.

  • @NamedNativeQuery là một annotation được sử dụng trong JPA (Java Persistence API) để định nghĩa một truy vấn SQL nguyên thủy (native query).

  • @Transactional: Annotation @Transactional được đặt trước một phương thức hoặc một lớp và chỉ định rằng phương thức hoặc tất cả các phương thức trong lớp được gọi trong một giao dịch (transaction). Giao dịch dùng để đảm bảo tính nhất quán của dữ liệu và bảo đảm rằng các thay đổi được thực hiện thành công hoặc rollback nếu có lỗi xảy ra. Annotation này giúp giảm thiểu việc viết mã để quản lý giao dịch thủ công và đảm bảo rằng các thay đổi dữ liệu được xử lý một cách an toàn và nhất quán.

  • @Modifying: Annotation @Modifying được sử dụng để chỉ định rằng phương thức đang thực hiện một hành động sửa đổi dữ liệu trong cơ sở dữ liệu, không phải là một truy vấn truy xuất dữ liệu. Annotation này cần được sử dụng kèm với annotation @Transactional.

  • @Query: Annotation @Query được sử dụng để định nghĩa truy vấn SQL hoặc JPQL (Java Persistence Query Language) mà phương thức sẽ thực hiện. Với nativeQuery = true, câu lệnh truy vấn được sử dụng là một câu lệnh SQL gốc (Native SQL) thay vì truy vấn JPQL. Các tham số của truy vấn được chỉ định bằng cách sử dụng các tham số có tên (?1, ?2, ...) và các tham số này sẽ được truyền vào từ các tham số của phương thức.

Tổng quan Swagger: Để sử dụng cơ bản thì Swagger cung cấp một số các Annotations hữu ích sau:

SYNTAX DESCRIPTION

SYNTAX DESCRIPTION
@Api Đánh dấu 1 class là nơi chứa các API
@ApiModel Đánh dấu 1 class là Swagger Model
@ApiModelProperty Bổ sung các thông tin cho
@ApiOperation Mô tả cho một API và response của nó
@ApiParam Mô tả các parameter
@ApiResponse Mô tả status code của response
@ApiResponses Mô tả danh sách các status code của response

GeneratedValue: và Trường sinh này áp dụng cho các trường kiểu số nguyên (integer) hoặc số dấu thập phân (decimal)

  • GenerationType.AUTO: là một kiểu sinh giá trị tự động. hệ thống sẽ cố gắng tự động xác định cách sinh giá trị tốt nhất dựa trên cơ sở dữ liệu bạn đang sử dụng.
@GeneratedValue(strategy = GenerationType.AUTO)
  • GenerationType.IDENTITY: Được sử dụng với các cơ sở dữ liệu hỗ trợ tự động tăng (auto-increment) như MySQL hoặc PostgreSQL. Khi bạn chọn kiểu này, cơ sở dữ liệu sẽ tự động tăng giá trị của trường này khi một bản ghi mới được thêm vào cơ sở dữ liệu.
@GeneratedValue(strategy = GenerationType.IDENTITY)

=> id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY
  • GenerationType.SEQUENCE: Sử dụng với cơ sở dữ liệu hỗ trợ chuỗi số (sequence) như Oracle. Bạn cần phải cung cấp tên của chuỗi số mà bạn muốn sử dụng.
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_sequence_name")
  • GenerationType.TABLE: Sử dụng với cơ sở dữ liệu không hỗ trợ tự động tăng hoặc chuỗi số. Hibernate (một triển khai JPA phổ biến) sẽ sử dụng một bảng tạm thời để theo dõi và sinh giá trị duy nhất cho trường.
@GeneratedValue(strategy = GenerationType.TABLE, generator = "your_table_generator_name")

Serializable and De-Serializable

image

Tạo database table tự động từ Hibernate Entity:

image

image

  • Hibernate tạo lược đồ tự động : spring.jpa.hibernate.ddl-auto=true
  • Nếu chúng ta vẫn muốn có cả Hibernate tạo lược đồ tự động kết hợp với tạo lược đồ dựa trên tập lệnh và tập hợp dữ liệu, chúng ta sẽ phải sử dụng: spring.jpa.defer-datasource-initialization=true
  • Khởi tạo cơ sở dữ liệu bằng tập lệnh, chúng ta sẽ phải sử dụng: spring.sql.init.mode=always

Cấu hình cơ sở dữ liệu bằng cách sử dụng Hibernate:

Spring cung cấp một thuộc tính JPA cụ thể mà Hibernate sử dụng để tạo DDL: spring.jpa.hibernate.ddl-auto.

  • create (tạo) : Trước tiên Hibernate bỏ các bảng hiện có và sau đó tạo các bảng mới.
  • update (cập nhật): Mô hình đối tượng được tạo dựa trên ánh xạ (Annotations hoặc XML) được so sánh với lược đồ hiện có, sau đó Hibernate cập nhật lược đồ theo sự khác biệt. Nó không bao giờ xóa các bảng hoặc cột hiện có ngay cả khi chúng không còn được ứng dụng yêu cầu.
  • create - drop (tạo - xóa): Tương tự như tạo, với việc bổ sung Hibernate sẽ xóa cơ sở dữ liệu sau khi tất cả các hoạt động hoàn thành; thường được sử dụng cho unit testing.
  • validate (xác thực): Hibernate chỉ xác nhận xem các bảng và cột có tồn tại hay không. Nếu không, nó sẽ ném ra một ngoại lệ.
  • none: Giá trị này sẽ ngay lập tức tắt tạo DDL.

Tùy chỉnh tạo lược đồ cơ sở dữ liệu:

  • always: luôn khởi tạo cơ sở dữ liệu
  • embedded: luôn khởi khởi tạo nếu một cơ sở dữ liệu nhúng đang được sử dụng. Đây là mặc định nếu giá trị thuộc tính không được chỉ định.
  • never: không bao giờ khởi tạo cơ sở dữ liệu

@SQL: Spring cũng cung cấp Annotatios @Sql - một cách khai báo để khởi tạo và điền vào lược đồ thử nghiệm của chúng ta.

@Sql({"/employees_schema.sql", "/import_employees.sql"})
public class SpringBootInitialLoadIntegrationTest {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Test
    public void testLoadDataForTestClass() {
        assertEquals(3, employeeRepository.findAll().size());
    }
}

SQL: Chú thích này có thể được sử dụng để gắn SQL query vào một lớp hoặc một phương thức trong dự án Spring Boot của bạn.

@SQL("SELECT * FROM users WHERE username = :username")
public User findUserByUsername(String username) {
    // Thực hiện truy vấn SQL ở đây
}

SQLConfig: Chú thích này có thể được sử dụng để cấu hình các thiết lập liên quan đến SQL query, ví dụ như cơ sở dữ liệu mặc định để sử dụng.

@SQLConfig(key = "production")
public class ProductionDatabaseConfig {
    // Cấu hình cho cơ sở dữ liệu sản xuất
}

Annotation Thông Dụng Trong Hibernate :

  1. @Entity : được sử dụng để chú thích một class là một Entity.
  • Thuộc tính name của @Entity là không bắt buộc.
  • Entity khớp với một bảng lấy theo tên theo thứ tự ưu tiên:
    • name trong @Table.
    • name trong @Entity.
    • name của class.
  1. @Table
  • Một table trong database có thể có nhiều ràng buộc unique (duy nhất). Chúng ta có thể sử dụng @Table để mô tả các ràng buộc này
  • @Table cho phép chú thích tên bảng thông qua thuộc tính name (thuộc tính này không bắt buộc).
  • Nếu không chỉ rõ tên bảng trong phần tử name, Hibernate sẽ dựa vào phần tử name của @Entity sau đó mới tới tên của class.
  1. @Column
  • @Column được sử dụng để chỉ định thông tin chi tiết của cột mà một field của entity sẽ được ánh xạ với một column trong database.
    • Thuộc tính name được sử dụng để chị định tên cột nào trong database map với tên field được chú thích.
    • Thuộc tính length cho phép kích thước của cột. @Column không chỉ rõ phần tử length, mặc định nó là 255.
    • Thuộc tính nullable cho phép cột được đánh dấu KHÔNG NULL khi schema được tạo ra. Giá trị nullable mặc định là true.
    • Thuộc tính unique cho phép cột được đánh dấu chỉ chứa các giá trị duy nhất.
  1. @Transient
  • Trong entity class có chứa một field mà field này không tồn tồn tại trong database. Khi đó chúng ta sẽ gặp lỗi “java.sql.SQLSyntaxErrorException: Unknown column ‘additionalPropery’ in ‘field list'”.
  • Để tránh lỗi này, chúng ta có thể sử dụng @Transient để thông báo rằng thuộc tính/ phương thức này không liên quan gì tới một cột nào dưới database. Khi đó, Hibernate sẽ bỏ qua field này.
  1. @Temporal
  • @Temporal sử dụng để chú thích cho cột dữ liệu ngày tháng và thời gian (date time).
  • Có 3 giá trị cho TemporalType:
    • TemporalType.DATE : chú thích cột sẽ lưu trữ ngày tháng năm (bỏ đi thời gian).
    • TemporalType.TIME : chú thích cột sẽ lưu trữ thời gian (Giờ phút giây).
    • TemporalType.TIMESTAMP : chú thích cột sẽ lưu trữ ngày tháng và cả thời gian.
  1. @Id
  • @Id được sử dụng để mô tả đây là Id (Identity) của Entity, nó tương đương với cột đó là khóa chính (Primary Key) của table trong database.
  1. @GeneratedValue
  • @GeneratedValue được sử dụng để Hibernate tự động tạo ra giá trị và gán vào cho một cột trong trường hợp insert mới một Entity vào database.
  1. @Lob
  • @Lob thường chú thích cùng với @Column để nói rằng cột đó có kiểu BLOB hoặc CLOB. Trong một số Database có phân biệt TINY, MEDIUM, LARGE BLOB/CLOB, còn một số database thì không.
    @Lob
    @Column(name = "avatar", nullable = true, length = Integer.MAX_VALUE)
    private byte[] avatar;
  1. @ManyToOne
  • @ManyToOne mô tả một quan hệ N-1 (Nhiều – Một), nó thường được sử dụng cùng với @JoinColumn.
  • FetchType.LAZY: LAZY nói với Hibernate rằng, hãy tải dữ liệu một cách lười biếng, nghĩa là chỉ tải khi được gọi (khi cần thiết).
  • FetchType.EAGER: EAGER nói với Hibernate rằng, hãy truy vấn toàn bộ các cột của bảng liên quan.
  1. @OneToMany
  • @OneToMany mô tả quan hệ 1-N (Một – Nhiều). Nó là đảo ngược của @ManyToOne, và vì vậy nó dựa vào @ManyToOne để định nghĩa ra @OneToMany.
  1. @OneToOne
  • @OneToOne mô tả quan hệ 1-1 (Một – Một).
  1. @ManyToMany
  • @ManyToMany mô tả quan hệ N-N (Nhiều – Nhiều).
@Entity
@Table
public class Role {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
     
    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
    private Set<User> users;
}
 
@Entity(name = "User")
@Table(name = "user")
public class User {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    // ...
     
    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "user_roles", 
        joinColumns = { @JoinColumn(name = "user_id", nullable = false, updatable = false) }, 
        inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false, updatable = false) })
    private Set<Role> roles;
}
  1. @OrderBy
  • @OrderBy được sử dụng để sắp xếp một danh sách, vì vậy nó có thể được sử dụng cùng với @OneToMany, @ManyToMany.
@Entity
@Table
public class Category {
 
    // ...
 
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "category")
    @OrderBy("title")
    private Set<Post> posts;
}

@Enumerated hỗ trợ hai kiểu ánh xạ Enum :

  • EnumType.ORDINAL: Khi sử dụng kiểu này, JPA sẽ lưu giá trị của enum dưới dạng số nguyên, tức là vị trí của hằng số enum trong khai báo
  • EnumType.STRING: Khi sử dụng kiểu này, JPA sẽ lưu giá trị của enum dưới dạng chuỗi, tức là tên của hằng số enum12. Điều này giúp giữ nguyên độ rõ ràng của mã nguồn và đảm bảo tính nhất quán khi thay đổi cấu trúc của enum
public enum Status {
    OPEN,
    CLOSED
}

@Entity
public class Article {
    @Id
    private int id;
    private String title;
    
    @Enumerated(EnumType.STRING)
    private Status status;
}

Tổng quan về OAuth trong việc xác thực người dùng từ hệ thống:

OAuth2 có 4 loại định danh chính:

  • Authorization Code
  • Resource Owner Password Credentials
  • Implicit
  • Client Credentials

Để xác thực thì cần:

  • Client Client ID: chuỗi ký tự được sử dụng để định danh ứng dụng.
  • Client Secret: là một chuỗi ký tự dùng cho việc xác thực Client khi ứng dụng yêu cầu truy cập thông tin tài khoản người dùng. Chuỗi này được giữ bí mật giữa Client và Authorization Server.

Có thể hiểu Client ID là username, Client Secret là password của Client đối với Authorization cũng được. 😄

Protocol Flow OAuth2:

 +--------+                               +---------------+
 |        |--(A)- Authorization Request ->|   Resource    |
 |        |                               |     Owner     |
 |        |<-(B)-- Authorization Grant ---|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(C)-- Authorization Grant -->| Authorization |
 | Client |                               |     Server    |
 |        |<-(D)----- Access Token -------|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(E)----- Access Token ------>|    Resource   |
 |        |                               |     Server    |
 |        |<-(F)--- Protected Resource ---|               |
 +--------+                               +---------------+

 +--------+                                           +---------------+
 |        |--(A)------- Authorization Grant --------->|               |
 |        |                                           |               |
 |        |<-(B)----------- Access Token -------------|               |
 |        |               & Refresh Token             |               |
 |        |                                           |               |
 |        |                            +----------+   |               |
 |        |--(C)---- Access Token ---->|          |   |               |
 |        |                            |          |   |               |
 |        |<-(D)- Protected Resource --| Resource |   | Authorization |
 | Client |                            |  Server  |   |     Server    |
 |        |--(E)---- Access Token ---->|          |   |               |
 |        |                            |          |   |               |
 |        |<-(F)- Invalid Token Error -|          |   |               |
 |        |                            +----------+   |               |
 |        |                                           |               |
 |        |--(G)----------- Refresh Token ----------->|               |
 |        |                                           |               |
 |        |<-(H)----------- Access Token -------------|               |
 +--------+           & Optional Refresh Token        +---------------+

JSON in Spring Annotaion:

  • @JsonSerialize: được sử dụng để chỉ định cách một trường hoặc thuộc tính của một đối tượng Java sẽ được chuyển đổi thành JSON khi sử dụng thư viện Jackson
  • @JsonInclude: được sử dụng để chỉ định rằng các trường có giá trị null sẽ không được bao gồm trong đầu ra JSON. và ngược lại nếu khác null sẽ được hiển thị ở đầu JSON.
  • @JsonSerialize(using = LocalDateTimeSerializer.class): Chú thích này định nghĩa rằng trường hoặc thuộc tính nơi nó được áp dụng sẽ được chuyển đổi từ kiểu dữ liệu LocalDateTime của Java thành chuỗi JSON bằng cách sử dụng một lớp serializer tùy chỉnh là LocalDateTimeSerializer.class. Lớp serializer này sẽ quyết định cách biểu diễn một đối tượng LocalDateTime thành chuỗi JSON.
  • @JsonDeserialize(using = LocalDateTimeDeserializer.class): Chú thích này định nghĩa rằng khi chuyển đổi từ chuỗi JSON thành đối tượng Java, trường hoặc thuộc tính nơi nó được áp dụng sẽ được chuyển đổi từ chuỗi JSON thành kiểu dữ liệu LocalDateTime của Java bằng cách sử dụng một lớp deserializer tùy chỉnh là LocalDateTimeDeserializer.class. Lớp deserializer này sẽ quyết định cách chuyển đổi chuỗi JSON thành một đối tượng LocalDateTime.

Sử dụng Model Mapper:

@Configuration
public class ModelMapperConfig {
    @Bean
    public ModelMapper modelMapper() {
        // Tạo object và cấu hình
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration()
                .setMatchingStrategy(MatchingStrategies.STRICT);
        return modelMapper;
    }
}

sau đó là dùng `model mapper' để map giữa entity với dto và ngược lại:

@Service
public class UserService {
    // Có thể inject bằng cách khác, mình viết như thế này cho gọn
    @Autowired
    private final ModelMapper mapper;

    public UserDto getUser(String username) {
        // Lấy User entity ra từ DB
        User user = userRepository.findByUsername(username);

        // Map thành DTO
        UserDto userDto = mapper.map(user, UserDto.class);
        
        return userDto;
    }
}

Các mức độ của mapping (hay Matching strategy):

có 3 loại mapping, mỗi loại có một hằng số biểu diễn (để set config):

  • Chiến lược map chuẩn: MatchingStrategies.STANDARD
  • Chiến lược map lỏng lẻo: MatchingStrategies.LOOSE
  • Chiến lược map chặt chẽ: MatchingStrategies.STRICT

Vài thuộc tính cấu hình căn bản như sau:

  • setSkipNullEnabled() có cho phép bỏ qua thuộc tính null hay không
  • setDeepCopyEnabled() mặc định dùng shallow copy, dùng deep copy thì sẽ chậm hơn.
  • setMatchingStrategy() đặt loại mapping (như phần trên)
  • setFieldAccessLevel() chỉ định field truy cập ở mức độ nào (private, public,...)
  • setMethodAccessLevel() chỉ định mức độ truy cập method, getter và setter (như trên)
  • setSourceNameTokenizer() chỉ định quy ước đặt tên cho thuộc tính ở source (object nguồn dùng để map)
  • setDestinationNameTokenizer() chỉ định quy ước đặt tên cho thuộc tính ở đích (object map ra).