blog/source/_posts/spring-security-webflux.md

7.6 KiB
Raw Blame History

title tags categories keywords date
在Spring WebFlux中的配置Spring Security
Java
Spring
Spring Security
Spring WebFlux
安全认证
JVM
Spring
Spring,Spring Security,UserDetails,SecurityContext,Http,WebFlux 2021-08-11 22:52:00

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

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

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

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

  1. {% post_link spring-security-basic %}
  2. {% post_link spring-security-webmvc %}
  3. {% post_link spring-security-webflux %}

配置类

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

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

@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配置的核心类。所以接下来看一下这个类提供的主要功能都有哪些。

{% oss_image spring-security/spring-security-ServerHttpSecurity.svg ServerHttpSecurity类结构图 %}

!!! note "" 在这个图中,同样省略了返回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接口的实现。这个接口被简化一下以后,就是这个样子。

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接口的形式非常类似,但是还有相当的不同。

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

@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>

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

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

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

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

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

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

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

ServerSecurityContextRepository

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

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

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

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