| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- package com.zsElectric.boot.config;
- import cn.binarywang.wx.miniapp.api.WxMaService;
- import cn.hutool.captcha.generator.CodeGenerator;
- import cn.hutool.core.util.ArrayUtil;
- import com.zsElectric.boot.common.util.electric.queryToken.ThirdPartyJwtAuthFilter;
- import com.zsElectric.boot.config.property.SecurityProperties;
- import com.zsElectric.boot.core.filter.RateLimiterFilter;
- import com.zsElectric.boot.security.filter.CaptchaValidationFilter;
- import com.zsElectric.boot.security.filter.TokenAuthenticationFilter;
- import com.zsElectric.boot.security.handler.MyAccessDeniedHandler;
- import com.zsElectric.boot.security.handler.MyAuthenticationEntryPoint;
- import com.zsElectric.boot.security.provider.SmsAuthenticationProvider;
- import com.zsElectric.boot.security.provider.WxMiniAppCodeAuthenticationProvider;
- import com.zsElectric.boot.security.provider.WxMiniAppPhoneAuthenticationProvider;
- import com.zsElectric.boot.security.provider.WxMiniAppPhoneCodeAuthenticationProvider;
- import com.zsElectric.boot.security.token.TokenManager;
- import com.zsElectric.boot.security.service.SysUserDetailsService;
- import com.zsElectric.boot.system.service.ConfigService;
- import com.zsElectric.boot.system.service.UserService;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.annotation.Order;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.authentication.ProviderManager;
- import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
- import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
- import org.springframework.security.config.annotation.web.builders.HttpSecurity;
- import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
- import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
- import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
- import org.springframework.security.config.http.SessionCreationPolicy;
- import org.springframework.security.crypto.password.PasswordEncoder;
- import org.springframework.security.web.SecurityFilterChain;
- import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
- /**
- * Spring Security 配置类
- *
- * @author Ray.Hao
- * @since 2023/2/17
- */
- @Slf4j
- @Configuration
- @EnableWebSecurity
- @EnableMethodSecurity
- @RequiredArgsConstructor
- public class SecurityConfig {
- private final RedisTemplate<String, Object> redisTemplate;
- private final PasswordEncoder passwordEncoder;
- private final TokenManager tokenManager;
- private final WxMaService wxMaService;
- private final UserService userService;
- private final SysUserDetailsService userDetailsService;
- private final CodeGenerator codeGenerator;
- private final ConfigService configService;
- private final SecurityProperties securityProperties;
- private final ThirdPartyJwtAuthFilter thirdPartyAuthFilter;
-
- // 注入 UserInfoService 用于小程序认证
- private final com.zsElectric.boot.business.service.UserInfoService userInfoService;
- // 仅针对第三方URL的安全过滤链,只挂第三方认证过滤器
- @Bean
- @Order(1)
- public SecurityFilterChain thirdPartySecurityFilterChain(HttpSecurity http) throws Exception {
- log.info("第三方认证过滤器: {}", thirdPartyAuthFilter);
- String[] thirdPartyUrls = securityProperties.getThirdPartyUrls();
- if (thirdPartyUrls == null || thirdPartyUrls.length == 0) {
- log.warn("第三方URL未配置或为空,使用占位符避免匹配所有请求");
- thirdPartyUrls = new String[]{"/__thirdparty_noop__"};
- }
- log.info("========== 配置第三方 SecurityFilterChain, urls: {} ==========", (Object) thirdPartyUrls);
- http
- .securityMatcher(thirdPartyUrls) // 只匹配第三方URL
- .authorizeHttpRequests(registry -> registry
- .anyRequest().authenticated()
- )
- .exceptionHandling(configurer ->
- configurer
- .authenticationEntryPoint(new MyAuthenticationEntryPoint())
- .accessDeniedHandler(new MyAccessDeniedHandler())
- )
- .sessionManagement(configurer ->
- configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- )
- .csrf(AbstractHttpConfigurer::disable)
- .formLogin(AbstractHttpConfigurer::disable)
- .httpBasic(AbstractHttpConfigurer::disable)
- .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
- // 只加第三方认证过滤器
- .addFilterBefore(thirdPartyAuthFilter, UsernamePasswordAuthenticationFilter.class);
- return http.build();
- }
- /**
- * 配置安全过滤链 SecurityFilterChain
- */
- @Bean
- @Order(2)
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
-
- log.info("========== 配置 SecurityFilterChain ==========");
- return http
- .authorizeHttpRequests(requestMatcherRegistry -> {
- // 配置无需登录即可访问的公开接口
- String[] ignoreUrls = securityProperties.getIgnoreUrls();
- log.info("安全白名单 ignore-urls: {}", (Object) ignoreUrls);
- if (ArrayUtil.isNotEmpty(ignoreUrls)) {
- requestMatcherRegistry.requestMatchers(ignoreUrls).permitAll();
- }
- // 配置完全绕过安全检查的路径(原 unsecuredUrls)
- String[] unsecuredUrls = securityProperties.getUnsecuredUrls();
- log.info("非安全端点 unsecured-urls: {}", (Object) unsecuredUrls);
- if (ArrayUtil.isNotEmpty(unsecuredUrls)) {
- requestMatcherRegistry.requestMatchers(unsecuredUrls).permitAll();
- }
- // 其他所有请求需登录后访问
- requestMatcherRegistry.anyRequest().authenticated();
- }
- )
- .exceptionHandling(configurer ->
- configurer
- .authenticationEntryPoint(new MyAuthenticationEntryPoint()) // 未认证异常处理器
- .accessDeniedHandler(new MyAccessDeniedHandler()) // 无权限访问异常处理器
- )
- // 禁用默认的 Spring Security 特性,适用于前后端分离架构
- .sessionManagement(configurer ->
- configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态认证,不使用 Session
- )
- .csrf(AbstractHttpConfigurer::disable) // 禁用 CSRF 防护,前后端分离无需此防护机制
- .formLogin(AbstractHttpConfigurer::disable) // 禁用默认的表单登录功能,前后端分离采用 Token 认证方式
- .httpBasic(AbstractHttpConfigurer::disable) // 禁用 HTTP Basic 认证,避免弹窗式登录
- // 禁用 X-Frame-Options 响应头,允许页面被嵌套到 iframe 中
- .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
- // 限流过滤器
- .addFilterBefore(new RateLimiterFilter(redisTemplate, configService), UsernamePasswordAuthenticationFilter.class)
- // 验证码校验过滤器
- .addFilterBefore(new CaptchaValidationFilter(redisTemplate, codeGenerator), UsernamePasswordAuthenticationFilter.class)
- // 验证和解析过滤器
- .addFilterBefore(new TokenAuthenticationFilter(tokenManager), UsernamePasswordAuthenticationFilter.class)
- .build();
- }
- /**
- * 配置Web安全自定义器,以忽略特定请求路径的安全性检查。
- * <p>
- * 该配置用于指定哪些请求路径不经过Spring Security过滤器链。通常用于静态资源文件。
- * 注意:这些警告可以忽略,因为Swagger等静态资源需要完全绕过过滤器链才能正常访问。
- */
- @Bean
- public WebSecurityCustomizer webSecurityCustomizer() {
- return (web) -> {
- String[] unsecuredUrls = securityProperties.getUnsecuredUrls();
- if (ArrayUtil.isNotEmpty(unsecuredUrls)) {
- web.ignoring().requestMatchers(unsecuredUrls);
- }
- };
- }
- /**
- * 默认密码认证的 Provider
- */
- @Bean
- public DaoAuthenticationProvider daoAuthenticationProvider() {
- DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(userDetailsService);
- daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
- return daoAuthenticationProvider;
- }
- /**
- * 微信小程序Code认证Provider
- */
- @Bean
- public WxMiniAppCodeAuthenticationProvider wxMiniAppCodeAuthenticationProvider() {
- return new WxMiniAppCodeAuthenticationProvider(userService, wxMaService);
- }
- /**
- * 微信小程序手机号认证Provider
- */
- @Bean
- public WxMiniAppPhoneAuthenticationProvider wxMiniAppPhoneAuthenticationProvider() {
- return new WxMiniAppPhoneAuthenticationProvider(userService, wxMaService);
- }
- /**
- * 微信小程序手机号Code认证Provider(新版接口)
- * <p>
- * 使用 UserInfoService 直接操作 c_user_info 表
- */
- @Bean
- public WxMiniAppPhoneCodeAuthenticationProvider wxMiniAppPhoneCodeAuthenticationProvider() {
- return new WxMiniAppPhoneCodeAuthenticationProvider(userInfoService, wxMaService);
- }
- /**
- * 短信验证码认证 Provider
- */
- @Bean
- public SmsAuthenticationProvider smsAuthenticationProvider() {
- return new SmsAuthenticationProvider(userService, redisTemplate);
- }
- /**
- * 认证管理器
- */
- @Bean
- public AuthenticationManager authenticationManager(
- DaoAuthenticationProvider daoAuthenticationProvider,
- WxMiniAppCodeAuthenticationProvider wxMiniAppCodeAuthenticationProvider,
- WxMiniAppPhoneAuthenticationProvider wxMiniAppPhoneAuthenticationProvider,
- WxMiniAppPhoneCodeAuthenticationProvider wxMiniAppPhoneCodeAuthenticationProvider,
- SmsAuthenticationProvider smsAuthenticationProvider
- ) {
- return new ProviderManager(
- daoAuthenticationProvider,
- wxMiniAppCodeAuthenticationProvider,
- wxMiniAppPhoneAuthenticationProvider,
- wxMiniAppPhoneCodeAuthenticationProvider,
- smsAuthenticationProvider
- );
- }
- }
|