5.3.1 保护请求
需要确保 /design 和 /orders 的请求仅对经过身份验证的用户可用;应该允许所有用户发出所有其他请求。下面的配置就是这样做的:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests()
.antMatchers("/design", "/orders").hasRole("USER")
.antMatchers("/", "/**").permitAll()
.and()
.build();
}
对 authorizeRequests()
的调用返回一个对象(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry),可以在该对象上指定 URL 路径和模式以及这些路径的安全需求。在这种情况下,指定两个安全规则:
- 对于 /design 和 /orders 的请求应该是授予 ROLEUSER 权限的用户的请求。传递给
hasRole()
的角色上不要包含“ROLE”前缀,否则会被hasRole()
认为有相应权限。 - 所有的请求都应该被允许给所有的用户。
这些规则的顺序很重要。首先声明的安全规则优先于后声明的安全规则。如果交换这两个安全规则的顺序,所有请求都将应用 permitAll()
,那么关于 /design 和 /orders 请求的规则将不起作用。
hasRole()
和 permitAll()
方法只是声明请求路径安全需求的两个方法。表 5.1 描述了所有可用的方法。
表 5.1 定义被保护路径的配置方法
方法 | 作用描述 |
---|---|
access(String) | 如果 SpEL 表达式的值为 true,则允许访问 |
anonymous() | 默认用户允许访问 |
authenticated() | 认证用户允许访问 |
denyAll() | 无条件拒绝所有访问 |
fullyAuthenticated() | 如果用户是完全授权的(不是记住用户),则允许访问 |
hasAnyAuthority(String...) | 如果用户有任意给定的权限,则允许访问 |
hasAnyRole(String...) | 如果用户有任意给定的角色,则允许访问 |
hasAuthority(String) | 如果用户有给定的权限,则允许访问 |
hasIpAddress(String) | 来自给定 IP 地址的请求允许访问 |
hasRole(String) | 如果用户有给定的角色,则允许访问 |
not() | 拒绝任何其他访问方法 |
permitAll() | 无条件允许访问 |
rememberMe() | 允许认证了的同时标记了记住我的用户访问 |
表 5.1 中的大多数方法为请求处理提供了基本的安全规则,但是它们是自我限制的,只支持那些方法定义的安全规则。或者,可以使用 access()
方法提供 SpEL 表达式来声明更丰富的安全规则。Spring Security 扩展了 SpEL,包括几个特定于安全性的值和函数,如表 5.2 所示。
表 5.2 Spring Security 对 SpEL 的扩展
Security 表达式 | 含义 |
---|---|
authentication | 用户认证对象 |
denyAll | 通常值为 false |
hasAnyAuthority(String… authorities) | 如果用户有任何一项授权,则为 true |
hasAnyRole(list of roles) | 如果用户有任何给定的角色,则为 true |
hasAuthority(String authority) | 如果用户有给定的授权,则为 true |
hasPermission(Object target, Object permission) | 如果用户有对给定目标的给定的授权,则为 true |
hasPermission(Object target, String targetType, Object permission) | 如果用户有对给定目标的给定的授权,则为 true |
hasRole(role) | 如果用户有给定的角色,则为 true |
hasIpAddress(IP Address) | 如果请求来自给定 IP 地址,则为 true |
isAnonymous() | 如果用户是默认用户,则为 true |
isAuthenticated() | 如果用户是认证了的,则为 true |
isFullyAuthenticated() | 如果用户被完全认证了的(不是使用记住我进行认证),则为 true |
isRememberMe() | 如果用户被标记为记住我后认证了,则为 true |
permitAll() | 通常值为 true |
principal | 用户 pricipal 对象 |
表 5.2 中的大多数安全表达式扩展对应于表 5.1 中的类似方法。实际上,使用 access()
方法以及 hasRole()
和 permitAll 表达式,可以按如下方式重写 SecurityFilterChain 配置。
程序清单 5.7 使用 Spring 表达式定义认证规则
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests()
.antMatchers("/design", "/orders").access("hasRole('USER')")
.antMatchers("/", "/**").access("permitAll()")
.and()
.build();
}
乍一看,这似乎没什么大不了的。毕竟,这些表达式只反映了已经对方法调用所做的工作。但是表达式可以灵活得多。例如,假设(出于某种疯狂的原因)只想允许具有 ROLE_USER 权限的用户在周二(例如,在周二)创建新的 Taco;您可以重写 SecurityFilterChain bean 方法如下:
@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();
}
使用基于 SpEL 的安全约束,这种可能性实际上是无限的。我敢打赌,您已经在构思基于 SpEL 的有趣的安全约束了。
只需使用 access()
和程序清单 5.9 中的 SpEL 表达式,就可以满足 Taco Cloud 应用程序的授权需求。现在,让我们来看看如何定制登录页面来适应 Taco Cloud 应用程序的外观。