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 应用程序的外观。

results matching ""

    No results matching ""