- 前言
- OAuth2 生态
- 关于 OAuth2
- Spring Security
- FilterChainProxy
- SecurityFilterChain 定义示例
- 总结
之前或多或少的接触过 Spring Security,最近有契机基于 Spring Security 搭建了一套较完整的 OAuth2 认证服务,对 Spring Security 对 OAuth2 支持的生态做了简单的了解,以此文分享
OAuth2 生态- 现在的 Spring Security 是独立完整的项目,囊括了对 oauth2 等的支持而不是基于之前的 Spring Security OAuth2 项目独立实现(原项目已 @Deprecated)
- Spring Security oauth2 模块支持 oauth2-client oauth2-resource-server oauth2-jose,不再提供 认证中心 的支持
- 认证中心由单独的新项目 spring-security-oauth2-authorization-server 支持 ,该项目是由 Spring Security 团队牵头的社区开源项目,目前正式版本仅为 0.2.3
- spring-security-oauth2-authorization-server 目前并没有或者说并没有特别成熟的 Spring Boot 自动装配支持,但实际上作为较新的项目,其配置方式已经很大程度上向 Spring Boot 靠拢(而不是传统的 @EnabledXXX 模式)
- 以上是当前 Spring Security(准确的说是 Spring)对 OAuth2 支持的最 新 生态,本着 技术就要用新的 的原则,个人项目完全基于该生态搭建
- 这里不会去聊 OAuth2 的细节,只想聊下 OAuth2 对我思考方式和对 【规范】二字理解的影响
- 说来说去,无论新的生态还是老的技术,无非是实现和使用上的区别,其底层的规范、理解还是基于 OAuth2 本身定义的,因此对这些生态组件的学习、理解很大程度就基于对规范的理解
- 规范往往看似通俗易懂却又字字细节,就像我自认为了解 OAuth2 规范,但又说不出个一二三来,这让我意识到规范是很值得 咬文嚼字 的东西
- 比如 OAuth2 最常规的模式:授权码模式 中,用户是基于代理(浏览器)被 Client 重定向 到 认证中心 与 认证中心 单独交互 从而保证授权码过程对 Client 无感,而不应该笼统的描述为 Client 从 认证中心 获取 授权码 ,两者相差甚远
- 这对学习、使用实现组件比如 Spring Security 时的影响是很大的,或者对是否、为什么选择 OAuth2 的何种模式都是很关键的
- 综上所述,如果想提高 OAuth2 生态组件的使用体验,首先深度的回顾一下 OAuth2 规范是很有必要的
- 贴一张 授权码模式 的序列图,思考一下
- Spring 体系下 OAuth2 的实现,前面提到,它提供 Client ResourceServer 的支持,AuthorizationServer 的部分由另一个项目支持
- 笼统地说,它基于一组一组的 Filter 实现,对应路由的一组 Filter 封装成一个 SecurityFilterChain,由我们以 Bean 组件的形式提供,最终以一个 FilterChainProxy 实例的形式注册到 Web 容器(Servlet)中
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
// 获取请求对应的 Filter 组
List filters = getFilters(firewallRequest);
// 如果没有就继续执行
if (filters == null || filters.size() == 0) {
firewallRequest.reset();
chain.doFilter(firewallRequest, firewallResponse);
return;
}
// 如果存在就执行这些 Filter
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}
------------------ getFilters ------------
private List getFilters(HttpServletRequest request) {
int count = 0;
// filterChains:即用户注册的所有 SecurityFilterChain
for (SecurityFilterChain chain : this.filterChains) {
// 根据路由匹配,返回匹到的第一个
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
FilterChainProxy 的部分源码:
- 拦截请求并获取对应的 (Spring Security)Filter 组,如果有就执行过滤逻辑
- 这些 Filter 就是普通的 Servlet Filter,通常有 Spring Security 内置或用户自定义,但它们不是直接注册到 Servlet 容器里,而是注册到 SecurityFilterChain 里
- getFilters 的逻辑就是遍历所有 SecurityFilterChain,返回第一个匹配 SecurityFilterChain 的 Filter 组,因此 SecurityFilterChain 的匹配器(RequestMather)和顺序定义很重要
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSFC(HttpSecurity httpSecurity) throws Exception {
// 匹配一组 AuthorizationServer 相关的路由
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(httpSecurity);
return httpSecurity
.formLogin()
.and()
.build();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
public SecurityFilterChain loginSFC(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.requestMatcher(new AntPathRequestMatcher(
"/open"
))
.authorizeHttpRequests()
// 放开 login
.anyRequest().permitAll()
.and()
// 关闭跨域
.csrf().disable()
.build();
}
@Bean
public SecurityFilterChain defaultWebSFC(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.authorizeHttpRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.build();
}
- 如上是 AuthorizationServer 的一段 SecurityFilterChain 配置示例
- @Order 注解指定对应的优先级,依次定义了三级过滤:
- AuthorizationServer 通过的对内置路由的处理,比如 /oauth2/authorize /oauth2/token 等路由忽略跨域等
- 自定义了一个指定了 RequestMatcher 的 SecurityFilterChain,处理 /open 端口
- 兜底 SecurityFilterChain 未指定 RequestMatcher,则匹配所有剩余的路由并处理它们:示例中的配置标识上述端口需要认证才能访问
- 断点下的优先级如下,则拦截到的请求返回第一个匹配到 SecurityFilterChain 的 Filter 组
- 本文从 Spring Security 对 OAuth2 的支持入手,简单的了解了下当前最 新 的生态环境
- OAuth2 规范的理解,对 Spring Security 的使用很有帮助
- 浅入浅出的聊了下 Spring Security 的架构,因此对 OAuth2 各组件的支持就是提供对应的 Filter
- SecurityFilterChain 式的安全策略配置应该是 Spring Security 与 Spring Security OAuth2 最大的不同(我猜的,后者我并没有完整的使用过)



