/* * * Copyright (c) 2018-2025, platformx All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the pig4cloud.com developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: platformx * */ package com.by4cloud.platformx.bootstrap; import com.by4cloud.platformx.auth.support.core.PlatformxDaoAuthenticationProvider; import com.by4cloud.platformx.auth.support.filter.PasswordDecoderFilter; import com.by4cloud.platformx.auth.support.filter.ValidateCodeFilter; import com.by4cloud.platformx.auth.support.handler.PlatformxAuthenticationFailureEventHandler; import com.by4cloud.platformx.auth.support.handler.PlatformxAuthenticationSuccessEventHandler; import com.by4cloud.platformx.auth.support.password.OAuth2ResourceOwnerPasswordAuthenticationProvider; import com.by4cloud.platformx.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider; import com.by4cloud.platformx.common.core.constant.SecurityConstants; import com.by4cloud.platformx.common.security.component.PermitAllUrlProperties; import com.by4cloud.platformx.common.security.component.PlatformxBearerTokenExtractor; import com.by4cloud.platformx.common.security.component.ResourceAuthExceptionEntryPoint; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer; import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import java.util.stream.Collectors; /** * @author platformx 认证授权服务器配置 */ @Configuration @RequiredArgsConstructor public class PlatformxBootSecurityServerConfiguration { private final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint; private final OpaqueTokenIntrospector customOpaqueTokenIntrospector; private final AuthenticationConverter accessTokenRequestConverter; private final OAuth2AuthorizationService authorizationService; private final PlatformxBearerTokenExtractor pigBearerTokenExtractor; private final PasswordDecoderFilter passwordDecoderFilter; private final OAuth2TokenGenerator oAuth2TokenGenerator; private final ValidateCodeFilter validateCodeFilter; private final PermitAllUrlProperties permitAllUrl; @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, PlatformxAuthenticationSuccessEventHandler successEventHandler, PlatformxAuthenticationFailureEventHandler failureEventHandler) throws Exception { OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); http.addFilterAfter(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterAfter(validateCodeFilter, UsernamePasswordAuthenticationFilter.class); http.apply(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点 tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter) // 注入自定义的授权认证Converter .accessTokenResponseHandler(successEventHandler) // 登录成功处理器 .errorResponseHandler(failureEventHandler);// 登录失败处理器 }).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证 oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常 .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面 .consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI))) .authorizationService(authorizationService) .authorizationServerSettings( AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()); AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls() .stream() .map(AntPathRequestMatcher::new) .collect(Collectors.toList()) .toArray(new AntPathRequestMatcher[]{}); http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers) .permitAll() .anyRequest() .authenticated()) .oauth2ResourceServer( oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector)) .authenticationEntryPoint(resourceAuthExceptionEntryPoint) .bearerTokenResolver(pigBearerTokenExtractor)) .exceptionHandling(configurer -> configurer.authenticationEntryPoint(resourceAuthExceptionEntryPoint)) .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .csrf(AbstractHttpConfigurer::disable); DefaultSecurityFilterChain securityFilterChain = http.build(); // 注入自定义授权模式实现 addCustomOAuth2GrantAuthenticationProvider(http); return securityFilterChain; } /** * 注入授权模式实现提供方 *
* 1. 密码模式 * 2. 短信登录 */ @SuppressWarnings("unchecked") private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) { AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class); OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider( authenticationManager, authorizationService, oAuth2TokenGenerator); OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider( authenticationManager, authorizationService, oAuth2TokenGenerator); // 处理 UsernamePasswordAuthenticationToken http.authenticationProvider(new PlatformxDaoAuthenticationProvider()); // 处理 OAuth2ResourceOwnerPasswordAuthenticationToken http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider); // 处理 OAuth2ResourceOwnerSmsAuthenticationToken http.authenticationProvider(resourceOwnerSmsAuthenticationProvider); } }