refactor: 移除匿名访问的设计,还原常用的白名单配置方案

This commit is contained in:
Ray.Hao
2024-12-25 22:38:15 +08:00
parent 1859a75819
commit eaf03138b9
14 changed files with 67 additions and 527 deletions

View File

@@ -2,9 +2,7 @@ package com.youlai.boot.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.collection.CollectionUtil;
import com.youlai.boot.common.enums.RequestMethodEnum;
import com.youlai.boot.common.util.AnonymousUtils;
import cn.hutool.core.util.ArrayUtil;
import com.youlai.boot.config.property.SecurityProperties;
import com.youlai.boot.core.filter.RateLimiterFilter;
import com.youlai.boot.core.security.exception.MyAccessDeniedHandler;
@@ -17,11 +15,9 @@ import com.youlai.boot.shared.auth.service.impl.JwtTokenService;
import com.youlai.boot.system.service.ConfigService;
import com.youlai.boot.system.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -36,9 +32,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.util.Map;
import java.util.Set;
/**
* Spring Security 安全配置
*
@@ -60,64 +53,61 @@ public class SecurityConfig {
private final SysUserDetailsService userDetailsService;
private final CodeGenerator codeGenerator;
private final SecurityProperties securityProperties;
private final ConfigService configService;
private final SecurityProperties securityProperties;
private final MyAuthenticationEntryPoint authenticationEntryPoint; // 项目内安全类
private final MyAccessDeniedHandler accessDeniedHandler;
private final ApplicationContext applicationContext;
/**
* 配置安全过滤链 SecurityFilterChain
*/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// 获取所有匿名访问路径
Map<String, Set<String>> anonymousUrls = AnonymousUtils.getAllAnonymousUrls(applicationContext);
http
.authorizeHttpRequests(requestMatcherRegistry ->
requestMatcherRegistry
// GET
.requestMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()
// POST
.requestMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()
// PUT
.requestMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()
// PATCH
.requestMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()
// DELETE
.requestMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()
// 所有类型的接口都放行
.requestMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll()
.anyRequest().authenticated()
return http
.authorizeHttpRequests(requestMatcherRegistry -> {
// 忽略认证的 URI 地址
String[] ignoreUrls = securityProperties.getIgnoreUrls();
if (ArrayUtil.isNotEmpty(ignoreUrls)) {
requestMatcherRegistry.requestMatchers(ignoreUrls).permitAll();
}
// 其他请求都需要认证
requestMatcherRegistry.anyRequest().authenticated();
}
)
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
httpSecurityExceptionHandlingConfigurer
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
.exceptionHandling(configurer ->
configurer
.authenticationEntryPoint(new MyAuthenticationEntryPoint()) // 未认证异常处理器
.accessDeniedHandler(new MyAccessDeniedHandler()) // 无权限访问异常处理器
)
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(AbstractHttpConfigurer::disable)
// 禁用默认的 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)
// JWT 验证和解析过滤器
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenService), UsernamePasswordAuthenticationFilter.class);
return http.build();
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenService), UsernamePasswordAuthenticationFilter.class)
.build();
}
/**
* 不走过滤器链的放行配置
* 配置Web安全自定义器以忽略特定请求路径的安全性检查。
* <p>
* 该配置用于指定哪些请求路径不经过Spring Security过滤器链。通常用于静态资源文件。
*/
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> {
if (CollectionUtil.isNotEmpty(securityProperties.getIgnoreUrls())) {
web.ignoring().requestMatchers(securityProperties.getIgnoreUrls().toArray(new String[0]));
String[] unsecuredUrls = securityProperties.getUnsecuredUrls();
if (ArrayUtil.isNotEmpty(unsecuredUrls)) {
web.ignoring().requestMatchers(unsecuredUrls);
}
};
}
@@ -130,7 +120,6 @@ public class SecurityConfig {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
return daoAuthenticationProvider;
}

View File

@@ -28,7 +28,9 @@ public class SecurityProperties {
/**
* 白名单 URL 集合
*/
private List<String> ignoreUrls;
private String[] ignoreUrls;
private String[] unsecuredUrls;
/**
* 会话属性