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 và /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 đó:
@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
/designvà/orderschỉ 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àohasRole(); phương thứchasRole()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 và /orders sẽ không có tác dụng.
Các phương thức hasRole() và 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
| Method | What 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 expression | What it evaluates to |
|---|---|
authentication | The user's authentication object |
denyAll | Always 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 |
principal | The 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() và 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
@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:
@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.
