Skip to content

5.3.1 Bảo vệ các yêu cầu

Bạn cần đảm bảo rằng các yêu cầu tới /design/orders chỉ có sẵn cho người dùng đã xác thực; tất cả các yêu cầu khác nên được cho phép với tất cả người dùng. Cấu hình sau đây thực hiện chính xác điều đó:

java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  return http
    .authorizeRequests()
      .antMatchers("/design", "/orders").hasRole("USER")
      .antMatchers("/", "/**").permitAll()

  .and()
  .build();
}

Lệnh gọi tới authorizeRequests() trả về một đối tượng (ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry) mà bạn có thể sử dụng để chỉ định các đường dẫn URL và mẫu cũng như các yêu cầu bảo mật cho các đường dẫn đó. Trong trường hợp này, bạn chỉ định hai quy tắc bảo mật sau:

  • Các yêu cầu tới /design/orders chỉ dành cho người dùng có quyền được cấp là ROLE_USER. Đừng bao gồm tiền tố ROLE_ trong tên vai trò khi truyền vào hasRole(); phương thức hasRole() sẽ tự động giả định điều đó.
  • Tất cả các yêu cầu còn lại được cho phép đối với tất cả người dùng.

Thứ tự của các quy tắc này rất quan trọng. Các quy tắc bảo mật được khai báo trước sẽ có ưu tiên cao hơn so với những quy tắc được khai báo sau. Nếu bạn đổi thứ tự của hai quy tắc trên, tất cả các yêu cầu sẽ được áp dụng permitAll() và quy tắc dành cho /design/orders sẽ không có tác dụng.

Các phương thức hasRole()permitAll() chỉ là hai trong số nhiều phương thức để khai báo yêu cầu bảo mật cho các đường dẫn. Bảng 5.1 mô tả tất cả các phương thức có sẵn.

Bảng 5.1: Các phương thức cấu hình để xác định cách bảo vệ đường dẫn

MethodWhat it does
access(String)Allows access if the given Spring Expression Language (SpEL) expression evaluates to true
anonymous()Allows access to anonymous users
authenticated()Allows access to authenticated users
denyAll()Denies access unconditionally
fullyAuthenticated()Allows access if the user is fully authenticated (not remembered)
hasAnyAuthority(String...)Allows access if the user has any of the given authorities
hasAnyRole(String...)Allows access if the user has any of the given roles
hasAuthority(String)Allows access if the user has the given authority
hasIpAddress(String)Allows access if the request comes from the given IP address
hasRole(String)Allows access if the user has the given role
not()Negates the effect of any of the other access methods
permitAll()Allows access unconditionally
rememberMe()Allows access for users who are authenticated via remember-me

Hầu hết các phương thức trong bảng 5.1 cung cấp các quy tắc bảo mật thiết yếu để xử lý yêu cầu, nhưng chúng có giới hạn, chỉ cho phép các quy tắc bảo mật như được định nghĩa bởi các phương thức đó. Ngoài ra, bạn có thể sử dụng phương thức access() để cung cấp một biểu thức SpEL nhằm khai báo các quy tắc bảo mật phong phú hơn. Spring Security mở rộng SpEL để bao gồm một số giá trị và hàm bảo mật đặc biệt, được liệt kê trong bảng 5.2.

Bảng 5.2: Các phần mở rộng của Spring Security cho Spring Expression Language

Security expressionWhat it evaluates to
authenticationThe user's authentication object
denyAllAlways evaluates to false
hasAnyAuthority(String… authorities)true if the user has been granted any of the given authorities
hasAnyRole(list of roles)true if the user has any of the given roles
hasAuthority(String authority)true if the user has been granted the specified authority
hasPermission(Object target, Object permission)true if the user has access to the specified target object for the given permission
hasPermission(Object target, String targetType, Object permission)true if the user has access to the object specified by targetId and the specified targetType for the given permission
hasRole(role)true if the user has the given role
hasIpAddress(IP Address)true if the request comes from the given IP address
isAnonymous()true if the user is anonymous
isAuthenticated()true if the user is authenticated
isFullyAuthenticated()true if the user is fully authenticated (not authenticated with remember-me)
isRememberMe()true if the user is authenticated via remember-me
permitAll()Always evaluates to true
principalThe user's principal object

Như bạn có thể thấy, hầu hết các phần mở rộng biểu thức bảo mật trong bảng 5.2 tương ứng với các phương thức tương tự trong bảng 5.1. Trên thực tế, bằng cách sử dụng phương thức access() cùng với các biểu thức hasRole()permitAll, bạn có thể viết lại cấu hình SecurityFilterChain như sau:

Liệt kê 5.7: Sử dụng biểu thức Spring để định nghĩa các quy tắc ủy quyền

java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  return http
    .authorizeRequests()
      .antMatchers("/design", "/orders").access("hasRole('USER')")
      .antMatchers("/", "/**").access("permitAll()")

    .and()
    .build();
}

Điều này ban đầu có thể không có vẻ quan trọng lắm. Sau cùng thì, những biểu thức này chỉ phản ánh những gì bạn đã làm bằng cách gọi phương thức. Nhưng biểu thức có thể linh hoạt hơn nhiều. Ví dụ, giả sử rằng (vì một lý do nào đó khá kỳ lạ) bạn muốn chỉ cho phép người dùng có quyền ROLE_USER tạo mới taco vào thứ Ba (ví dụ: Taco Tuesday); bạn có thể viết lại biểu thức như được thể hiện trong phiên bản đã chỉnh sửa của phương thức bean SecurityFilterChain:

java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  return http
    .authorizeRequests()
      .antMatchers("/design", "/orders")
        .access("hasRole('USER') && " +
          "T(java.util.Calendar).getInstance().get("+
          "T(java.util.Calendar).DAY_OF_WEEK) == " +
          "T(java.util.Calendar).TUESDAY")
      .antMatchers("/", "/**").access("permitAll")

    .and()
    .build();
}

Với các ràng buộc bảo mật SpEL, khả năng gần như là vô tận. Tôi cá rằng bạn đã bắt đầu nghĩ ra những quy tắc bảo mật thú vị dựa trên SpEL rồi.

Nhu cầu ủy quyền của ứng dụng Taco Cloud đã được đáp ứng một cách đơn giản bằng việc sử dụng access() và các biểu thức SpEL. Giờ hãy cùng xem cách tùy chỉnh trang đăng nhập để phù hợp với giao diện của ứng dụng Taco Cloud.

Released under the MIT License.