1.2.1
@Configuration(proxyBeanMethods = false)
public class OAuth2AuthorizationServerConfiguration {
// 默认授权服务器安全过滤器链
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
applyDefaultSecurity(http);
return http.build();
}
// 应用默认安全配置
public static void applyDefaultSecurity(HttpSecurity http) throws Exception {
// 授权服务器配置器
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
// 授权服务器端点请求匹配器
RequestMatcher endpointsMatcher = authorizationServerConfigurer
.getEndpointsMatcher();
http
// 仅对授权服务器端点请求进行安全配置
.securityMatcher(endpointsMatcher)
.authorizeHttpRequests(authorize ->
authorize.anyRequest().authenticated()
)
// 对授权服务器端点关闭csrf保护
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.apply(authorizationServerConfigurer);
}
// 默认JWT解码器
public static JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
// 添加支持的算法
Set<JWSAlgorithm> jwsAlgs = new HashSet<>();
jwsAlgs.addAll(JWSAlgorithm.Family.RSA);
jwsAlgs.addAll(JWSAlgorithm.Family.EC);
jwsAlgs.addAll(JWSAlgorithm.Family.HMAC_SHA);
// JWT处理器,负责处理签名/加密/明文的jwt
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
JWSKeySelector<SecurityContext> jwsKeySelector =
new JWSVerificationKeySelector<>(jwsAlgs, jwkSource);
jwtProcessor.setJWSKeySelector(jwsKeySelector);
// 覆盖Nimbus默认的JWT声明校验器,不对声明进行校验
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
});
return new NimbusJwtDecoder(jwtProcessor);
}
@Bean
RegisterMissingBeanPostProcessor registerMissingBeanPostProcessor() {
RegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor();
postProcessor.addBeanDefinition(AuthorizationServerSettings.class, () -> AuthorizationServerSettings.builder().build());
return postProcessor;
}
}
public final class OAuth2AuthorizationServerConfigurer
extends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer, HttpSecurity> {
// 所有授权服务器端点配置器
private final Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = createConfigurers();
// 匹配所有授权服务器端点对应的请求,以及JwkSet端点请求
private RequestMatcher endpointsMatcher;
...
// 启用OpenID Connect 1.0支持(默认关闭)
public OAuth2AuthorizationServerConfigurer oidc(Customizer<OidcConfigurer> oidcCustomizer) {
OidcConfigurer oidcConfigurer = getConfigurer(OidcConfigurer.class);
if (oidcConfigurer == null) {
addConfigurer(OidcConfigurer.class, new OidcConfigurer(this::postProcess));
oidcConfigurer = getConfigurer(OidcConfigurer.class);
}
oidcCustomizer.customize(oidcConfigurer);
return this;
}
...
// 配置器初始化
@Override
public void init(HttpSecurity httpSecurity) {
// 获取授权服务器设置(各端点url)
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
// 校验issuerUri
validateAuthorizationServerSettings(authorizationServerSettings);
// 处理OpenID Connect认证请求
if (isOidcEnabled()) {
// 如果启用OpenID Connect 1.0
// 添加 OpenID Connect 会话跟踪能力
initSessionRegistry(httpSecurity);
SessionRegistry sessionRegistry = httpSecurity.getSharedObject(SessionRegistry.class);
// 授权端点设置会话认证策略
OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer =
getConfigurer(OAuth2AuthorizationEndpointConfigurer.class);
authorizationEndpointConfigurer.setSessionAuthenticationStrategy((authentication, request, response) -> {
// 如果认证请求是使用授权码模式的OAuth2认证请求,且scope包含openid,则将会话注册到会话注册表
if (authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {
if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {
if (sessionRegistry.getSessionInformation(request.getSession().getId()) == null) {
sessionRegistry.registerNewSession(
request.getSession().getId(),
((Authentication) authorizationCodeRequestAuthentication.getPrincipal()).getPrincipal());
}
}
}
});
} else {
// 如果OpenID Connect 没有启用.
// 添加认证校验器,拒绝scope包含openid的认证请求
OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer =
getConfigurer(OAuth2AuthorizationEndpointConfigurer.class);
authorizationEndpointConfigurer.addAuthorizationCodeRequestAuthenticationValidator((authenticationContext) -> {
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
authenticationContext.getAuthentication();
if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,
"OpenID Connect 1.0 authentication requests are restricted.",
"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1");
throw new OAuth2AuthorizationCodeRequestAuthenticationException(
error, authorizationCodeRequestAuthentication);
}
});
}
// 构造授权端点请求匹配器
List<RequestMatcher> requestMatchers = new ArrayList<>();
// 添加每个端点对应的匹配规则
this.configurers.values().forEach(configurer -> {
configurer.init(httpSecurity);
requestMatchers.add(configurer.getRequestMatcher());
});
// 添加JwkSet端点请求匹配规则
requestMatchers.add(new AntPathRequestMatcher(
authorizationServerSettings.getJwkSetEndpoint(), HttpMethod.GET.name()));
this.endpointsMatcher = new OrRequestMatcher(requestMatchers);
// 当令牌获取/内省/撤回/设备认证端点发生访问拒绝异常或者认证异常时返回401未授权响应
ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = httpSecurity.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionHandling != null) {
exceptionHandling.defaultAuthenticationEntryPointFor(
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
new OrRequestMatcher(
getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class),
getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class),
getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class))
);
}
}
// 执行安全配置
@Override
public void configure(HttpSecurity httpSecurity) {
// 应用各端点配置器
this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity));
// 获取授权服务器设置
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
// 添加授权服务器上下文过滤器,负责将上下文设置到AuthorizationServerContextHolder
AuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter(authorizationServerSettings);
httpSecurity.addFilterAfter(postProcess(authorizationServerContextFilter), SecurityContextHolderFilter.class);
// 添加JwkSet端点过滤器
JWKSource<com.nimbusds.jose.proc.SecurityContext> jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity);
if (jwkSource != null) {
NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(
jwkSource, authorizationServerSettings.getJwkSetEndpoint());
httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
}
}
// 创建OAuth2服务器端点配置器
private Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> createConfigurers() {
Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();
// 客户端认证
configurers.put(OAuth2ClientAuthenticationConfigurer.class, new OAuth2ClientAuthenticationConfigurer(this::postProcess));
// 授权服务器元数据端点
configurers.put(OAuth2AuthorizationServerMetadataEndpointConfigurer.class, new OAuth2AuthorizationServerMetadataEndpointConfigurer(this::postProcess));
// 授权端点
configurers.put(OAuth2AuthorizationEndpointConfigurer.class, new OAuth2AuthorizationEndpointConfigurer(this::postProcess));
// 令牌获取端点
configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess));
// 令牌内省端点
configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess));
// 令牌撤回端点
configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess));
// 设备授权端点
configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class, new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess));
// 设备校验端点
configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess));
return configurers;
}
}