其实在前两篇文章中如果已经明白了Spring Security在Spring MVC中的配置,那么再去理解Spring Security在Spring WebFlux中的配置就十分容易了。这是因为在Spring WebFlux中的配置与Spring MVC中的配置原理基本相似,只是换了一套类而已。
与在Spring MVC中一样,要完成Spring Security配置,需要至少完成以下两项内容:
- 完成设置HTTP的认证流程。
- 完成认证所需要使用的类。
以下将从这两个方向分别说明如何在WebFlux应用中配置Spring Security。
配置类
相比Spring MVC,WebFlux中的配置类已经变得十分简单了,已经被简化成了一个非常普通的使用@Configuration
标记的Bean,而WebFlux的配置方法也是只需要形成一个SecurityWebFilterChain
类的Bean即可。要形成SecurityWebFilterChain
类的Bean,只需要使用ServerHttpSecurity
类的实例的build()
方法即可。
以下是一个比较简单的配置类示例。
|
|
从这个小示例可以看出,ServerHttpSecurity
类是WebFlux中完成Spring Security配置的核心类。所以接下来看一下这个类提供的主要功能都有哪些。
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
接口的实现。这个接口被简化一下以后,就是这个样子。
|
|
这里需要对WebFlux中的Exchange的概念进行一下解释。在WebFlux中,请求和相应都是被保存在ServerWebExchange
实例中的。所以所有定义的WebFilter也都是对传入的ServerWebExchange
实例进行修改和调整。除此之外,ServerWebExchange
中还包括了WebSession
和Principal
等通常在处理Web请求时所需要用到的内容。
ReactiveAuthenticationManager
在之前的文章中也提到过,WebFlux中不同的认证过滤器都采用了不同的AuthenticationManager,与Spring MVC不同的是,WebFlux中的AuthenticationManager需要实现的是ReactiveAuthenticationManager
接口,这个接口的形式与用于Spring MVC的AuthenticationManager
接口的形式非常类似,但是还有相当的不同。
以下是这个接口被简化以后的样子。
|
|
这是一个函数式接口,也就是说我们在实现这个接口的时候可以直接使用一个Lambda表达式来代替一个类作为这个接口的实现。这也是WebFlux中函数式编程概念的体现。这个借口所返回的Authentication
实例与Spring Security在Spring MVC中的特性一样,都是增加了对于用户认证信息和认证结果的注入。
利用WebFlux中不同的WebFilter会使用的不同的AuthenticationManager的特点,就可以在Spring中通过直接定义不同的Bean来为不同的WebFilter提供服务了。
UserDetailsService
在许多教程中,都会创建一个ReactiveUserDetailsService
接口的实例,这个接口的功能与用于Spring MVC的UserDetailsService
接口的功能一样,但是接口中方法的返回值换成了WebFlux中的Mono<UserDetails>
。
现在这个接口被简化以后是下面这个样子。
|
|
在这些教程中,主要是通过ServerHttpSecurityConfiguration
类用到了UserDetailsRepositoryReactiveAuthenticationManager
,而这个被用到的AuthenticationManager类又依赖到了一个实现了ReactiveUserDetailsService
接口的Bean。这样就可以为整个登录认证过程提供一个默认的获取用户详细信息的方法。
另一个接口名称跟ReactiveUserDetailsService
比较相似的是ReactiveUserDetailsPasswordService
接口。但是这个接口的功能却不是用于获取用户详细信息或者是对用户进行认证,而是用于修改密码。这个接口的定义如下。
|
|
所以这两个接口在使用的时候,不要弄混。ReactiveUserDetailsPasswordService
接口的实例也是在ServerHttpSecurityConfiguration
类中被使用的,所以也可以通过定义一个Bean来在应用中使用它。
PasswordEncoder
,它也是可以通过定义一个Bean来在应用中使用。
ServerSecurityContextRepository
不止在ServerHttpSecurity
类中,还有其他的不少类中都可以看到ServerSecurityContextRepository
接口的身影。这个接口用于从请求信息中载入验证信息,也就是从请求中获取Token并组装成Authentication,并完成认证过程;也可以用于将保存了认证信息的SecurityContext
保存起来。
ServerSecurityContextRepository
接口的定义可以简化成以下样子。
|
|
从这个接口的定义可以看出,Spring Security在保存SecurityContext实例的时候,是将其与SeerverWebExchange
相对应着保存的,也就是说,通过一个ServerWebExchange
的实例,可以获取到相应的SecurityContext
,也就获得了其中保存的Authentication
等内容。