在Spring WebFlux中的配置Spring Security

发布时间:2021-08-11 22:52
最后更新:2021-08-11 22:52
所属分类:
JVM Spring

其实在前两篇文章中如果已经明白了Spring Security在Spring MVC中的配置,那么再去理解Spring Security在Spring WebFlux中的配置就十分容易了。这是因为在Spring WebFlux中的配置与Spring MVC中的配置原理基本相似,只是换了一套类而已。

与在Spring MVC中一样,要完成Spring Security配置,需要至少完成以下两项内容:

  1. 完成设置HTTP的认证流程。
  2. 完成认证所需要使用的类。

以下将从这两个方向分别说明如何在WebFlux应用中配置Spring Security。

  1. Spring Security基础
  2. 在Spring MVC中的配置Spring Security
  3. 在Spring WebFlux中的配置Spring Security

配置类

相比Spring MVC,WebFlux中的配置类已经变得十分简单了,已经被简化成了一个非常普通的使用@Configuration标记的Bean,而WebFlux的配置方法也是只需要形成一个SecurityWebFilterChain类的Bean即可。要形成SecurityWebFilterChain类的Bean,只需要使用ServerHttpSecurity类的实例的build()方法即可。

以下是一个比较简单的配置类示例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Configuration
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
  @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange(exchanges -> exchanges
                .anyExchange().authenticated()
            )
            .httpBasic(withDefaults())
            .formLogin(withDefaults());
        return http.build();
    }
}

从这个小示例可以看出,ServerHttpSecurity类是WebFlux中完成Spring Security配置的核心类。所以接下来看一下这个类提供的主要功能都有哪些。

ServerHttpSecurity类结构图
ServerHttpSecurity类结构图
在这个图中,同样省略了返回ServerHttpSecurity的采用Customizer进行配置的方法,其使用方法与同名配置方法无异。

ServerHttpSecurity类的结构图中可以看出来,相比Spring MVC中的HttpSecurity,已经简化了很多使用方法。但是不管发生了什么样的变化,在完成Spring Security配置的过程和所需要配置的内容是不变的,依旧要按照功能需要进行组合。

WebFilter

WebFilter接口是WebFlux中的重要功能,承担着与Spring MVC框架中Servlet Filter相同的功能。Spring Security在WebFlux中功能的实现,也是依靠多种实现了WebFilter接口的类。

其实与Spring MVC相似,Spring Security的ServerHttpSecurity类中也提供了将自定义WebFilter加入到请求处理流程中的功能,这就是addFilterAt()addFilterBefore()addFilterAfter()三个方法。但是与配置Spring MVC中的Servlet Filter顺序不同的是,WebFlux中的WebFilter已经被定义好了顺序,都保存在了SecurityWebFilterOrder枚举里,所以在加入自定义WebFilter的时候可以直接选择指定的位置即可,不必再去寻找实际的Filter类。

Spring Security中定义的用于Security处理的WebFilter没有什么特殊的,完全就是普通的WebFilter接口的实现。这个接口被简化一下以后,就是这个样子。

1
2
3
public interface WebFilter {
  Mono<void> filter(ServerWebExchange exchange, WebFilterChain chain);
}

这里需要对WebFlux中的Exchange的概念进行一下解释。在WebFlux中,请求和相应都是被保存在ServerWebExchange实例中的。所以所有定义的WebFilter也都是对传入的ServerWebExchange实例进行修改和调整。除此之外,ServerWebExchange中还包括了WebSessionPrincipal等通常在处理Web请求时所需要用到的内容。

ReactiveAuthenticationManager

在之前的文章中也提到过,WebFlux中不同的认证过滤器都采用了不同的AuthenticationManager,与Spring MVC不同的是,WebFlux中的AuthenticationManager需要实现的是ReactiveAuthenticationManager接口,这个接口的形式与用于Spring MVC的AuthenticationManager接口的形式非常类似,但是还有相当的不同。

以下是这个接口被简化以后的样子。

1
2
3
4
@FunctionalInterface
public interface ReactiveAuthenticationManager {
  Mono<Authentication> authenticate(Authentication authentication);
}

这是一个函数式接口,也就是说我们在实现这个接口的时候可以直接使用一个Lambda表达式来代替一个类作为这个接口的实现。这也是WebFlux中函数式编程概念的体现。这个借口所返回的Authentication实例与Spring Security在Spring MVC中的特性一样,都是增加了对于用户认证信息和认证结果的注入。

利用WebFlux中不同的WebFilter会使用的不同的AuthenticationManager的特点,就可以在Spring中通过直接定义不同的Bean来为不同的WebFilter提供服务了。

UserDetailsService

在许多教程中,都会创建一个ReactiveUserDetailsService接口的实例,这个接口的功能与用于Spring MVC的UserDetailsService接口的功能一样,但是接口中方法的返回值换成了WebFlux中的Mono<UserDetails>

现在这个接口被简化以后是下面这个样子。

1
2
3
public interface ReactiveUserDetailsService {
  Mono<UserDetails> findByUsername(String username);
}

在这些教程中,主要是通过ServerHttpSecurityConfiguration类用到了UserDetailsRepositoryReactiveAuthenticationManager,而这个被用到的AuthenticationManager类又依赖到了一个实现了ReactiveUserDetailsService接口的Bean。这样就可以为整个登录认证过程提供一个默认的获取用户详细信息的方法。

另一个接口名称跟ReactiveUserDetailsService比较相似的是ReactiveUserDetailsPasswordService接口。但是这个接口的功能却不是用于获取用户详细信息或者是对用户进行认证,而是用于修改密码。这个接口的定义如下。

1
2
3
public interface ReactiveUserDetailsPasswordService {
  Mono<UserDetails> updatePassword(UserDetails user, String newPassword);
}

所以这两个接口在使用的时候,不要弄混。ReactiveUserDetailsPasswordService接口的实例也是在ServerHttpSecurityConfiguration类中被使用的,所以也可以通过定义一个Bean来在应用中使用它。

与此相同的还有PasswordEncoder,它也是可以通过定义一个Bean来在应用中使用。

ServerSecurityContextRepository

不止在ServerHttpSecurity类中,还有其他的不少类中都可以看到ServerSecurityContextRepository接口的身影。这个接口用于从请求信息中载入验证信息,也就是从请求中获取Token并组装成Authentication,并完成认证过程;也可以用于将保存了认证信息的SecurityContext保存起来。

ServerSecurityContextRepository接口的定义可以简化成以下样子。

1
2
3
4
public interface ServerSecurityContextRepository {
  Mono<Void> save(ServerWebExchange exchange, SecurityContext context);
  Mono<SecurityContext> load(SeerverWebExchange exchange);
}

从这个接口的定义可以看出,Spring Security在保存SecurityContext实例的时候,是将其与SeerverWebExchange相对应着保存的,也就是说,通过一个ServerWebExchange的实例,可以获取到相应的SecurityContext,也就获得了其中保存的Authentication等内容。


索引标签
Java
Spring
Spring Security
Spring WebFlux
安全认证