Spring Security OAuth2 Resource Server 的一些核心组件和内置 Filter
前言
Spring Security OAuth2
主要提供 4
个模块,其中 spring-security-oauth2-resource-server
提供 OAuth2 Resource Server
的实现,本文主要了解一些核心组件和部分内置 Filter
核心组件
SecurityFilterChain
--------------- Spring Boot 自动装配
@Bean
@ConditionalOnBean(JwtDecoder.class)
SecurityFilterChain jwtSecurityFilterChain(HttpSecurity http) throws Exception {
// 所有请求需要认证
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
// 声明 资源服务器 验证的令牌格式,默认 JWT
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
------------------ 自定义
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
return security
.authorizeRequests(
request -> request
.mvcMatchers("/test").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(
// 指定令牌解析的方式,比如:从 Request Head 的 token 获取
// 默认在 Request Head 中以 Authorization: Bearer token 的格式提供
config -> config.bearerTokenResolver(new HeaderBearerTokenResolver("token"))
.jwt()
)
.build();
}
SecurityFilterChain
是Spring Security
本身的组件- 此处是
Spring Boot
自动装配类提供的默认实例,其中oauth2ResourceServer
声明为Resource Server
OAuth2ResourceServerConfigurer::jwt
指定资源服务器
验证的令牌格式为JWT
- 当然也可以提供自己的
SecurityFilterChain
,比如自行指定解析令牌的方式:默认从Request Head
中以Authorization: Bearer token
的格式解析
JwtDecoder
@Bean
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
JwtDecoder jwtDecoderByJwkKeySetUri() {
// 基于 JwkSetUri 等创建对应的 NimbusJwtDecoder
NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
.jwsAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
String issuerUri = this.properties.getIssuerUri();
if (issuerUri != null) {
nimbusJwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri));
}
return nimbusJwtDecoder;
}
Resource Server
默认接受的令牌格式为JWT
,因此Spring Boot
会默认装配一个基于JwkSetUri
属性的NimbusJwtDecoder
实例JwkSetUri
属性即对应认证中心
的/oauth2/jwks
路径,主要是获取验签JWT
的密钥信息
JwtAuthenticationConverter
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
// 从 JWT 中解析的权限信息不加前缀
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
JwtAuthenticationConverter
实例负责从JWT
中转换认证
信息- 其中,
JwtGrantedAuthoritiesConverter
负责转换权限
信息 - 一般情况这个实例不需要自定义覆盖,但是如果有需求也可以调整,比如示例中从
JWT
中转换出来的权限不需要前缀(默认前缀SCOPE_
,即资源服务器
声明权限时需要带上此前缀)
内置 Filter
BearerTokenAuthenticationFilter
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token;
try {
// 从 request 中解析 token 信息
token = this.bearerTokenResolver.resolve(request);
}
catch (OAuth2AuthenticationException invalid) {
return;
}
// 构造 BearerTokenAuthenticationToken
BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
try {
// 令牌验证
AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authenticationResult);
SecurityContextHolder.setContext(context);
this.securityContextRepository.saveContext(context, request, response);
filterChain.doFilter(request, response);
}
catch (AuthenticationException failed) {
}
}
不难猜测,该过滤器负责处理需要认证的路径:解析并验证 令牌
信息
总结
spring-security-oauth2-resource-server
的组件和 Filter
相对简单,只需要负责正确的解析 令牌
并验证,在 Spring Boot
自动装配的加持下,只需要正确指定 spring.security.oauth2.resourceserver.jwt.issuer-uri
属性即可