maven依赖<!-- security启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>3.3.2</version> </dependency> <!-- 依赖web环境 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>provided</scope> <version>3.3.2</version> </dependency> 配置对@PermitAll接口放行配置WebSecurityConfigurerAdapter@AutoConfiguration @AutoConfigureOrder(-1) // 目的:先于 Spring Security 自动配置,避免一键改包后,org.* 基础包无法生效 @EnableMethodSecurity(securedEnabled = true) // 方法级别安全性控制 public class CustomWebSecurityConfigurerAdapter{ @Resource private ApplicationContext applicationContext; /** * 在 Spring Security 5.7 之后通过注册SecurityFilterChain的Bean方式替代依赖实现WebSecurityConfigurerAdapter接口的方式 * @param httpSecurity * @return * @throws Exception */ @Bean protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception{ // 通用配置 httpSecurity // 开启跨域 .cors(Customizer.withDefaults()) // CSRF 禁用,因为不使用 Session .csrf(AbstractHttpConfigurer::disable); // 获得 @PermitAll 带来的 URL 列表,免登录 Multimap<HttpMethod, String> permitAllUrls = getPermitAllUrlsFromAnnotations(); httpSecurity // ①:全局共享规则 .authorizeHttpRequests(c ->c // 1.1 静态资源,可匿名访问 .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll() // 1.2 设置 @PermitAll 无需认证 .requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll() .requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll() // 放行 Knife4j 和 Swagger 相关的 URL .requestMatchers( "/swagger-ui/**", "/swagger-resources/**", "/v3/api-docs/**", "/webjars/**", "/doc.html" ).permitAll()) // ③:兜底规则,必须认证 .authorizeHttpRequests(c -> c.anyRequest().authenticated()); return httpSecurity.build(); } /** * Multimap:Google 的 Guava 库,提供.类似于 Map,但每个键可以关联多个值 * */ private Multimap<HttpMethod,String> getPermitAllUrlsFromAnnotations(){ Multimap<HttpMethod, String> result = HashMultimap.create(); // 获得接口对应的 HandlerMethod 集合 RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class); // 获取所有的 HandlerMethod 映射关系 Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods(); // 筛选出带有@PermitAll注解的接口 for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) { RequestMappingInfo requestMappingInfo = entry.getKey(); HandlerMethod handlerMethod = entry.getValue(); // 如果该接口没有@PermitAll注解 if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) continue; Set<String> urls = new HashSet<>(); // 多路径的情况 @PostMapping({"/login","/hello","zef"}) if (Objects.nonNull(requestMappingInfo.getPatternsCondition())) urls.addAll(entry.getKey().getPatternsCondition().getPatterns()); // 单路径的情况 @PostMapping("/login") if (Objects.nonNull(requestMappingInfo.getPathPatternsCondition())) urls.addAll(convertList(requestMappingInfo.getPathPatternsCondition().getPatterns(), PathPattern::getPatternString)); if (urls.isEmpty())continue; // 根据请求方法,添加到 result 结果 requestMappingInfo.getMethodsCondition().getMethods().forEach(requestMethod -> { switch (requestMethod) { case GET: result.putAll(HttpMethod.GET, urls); break; case POST: result.putAll(HttpMethod.POST, urls); break; case PUT: result.putAll(HttpMethod.PUT, urls); break; case DELETE: result.putAll(HttpMethod.DELETE, urls); break; case HEAD: result.putAll(HttpMethod.HEAD, urls); break; case PATCH: result.putAll(HttpMethod.PATCH, urls); break; } }); } return result; } public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) { if (CollUtil.isEmpty(from)) { return new ArrayList<>(); } return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); } } 配置 spring.factories具体关于SpringBoot自定义Starter的实现思路创建项目:首先创建一个新 Maven 项目,命名为 xxx-spring-boot-starter添加依赖:确保spring-boot-autoconfigure依赖已添加,以支持自动配置功能。配置类:编写AutoConfiguration配置类,并使用注解@Configuration和@ConditionlOnMissingBean来实现有条件加载Bean定义功能:比如要封装一个特定的服务(如短信发送服务),定义接口和实现类,将其逻辑写入@Service标注的实现类中自动配置:在META-INF/spring/org.springframework.boot.autocnfigure.AutoConfiguration.imports中指定自动配置类的位置。与SpringBoot2.X不同,SpringBoot3.X不再使用sping.factories文件。完成以上配置 我们可以在Controller类上添加注解将会被Spring Security放行了@PostMapping("/login") @PermitAll @Operation(summary = "使用账号密码登录") public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) { return success(authService.login(reqVO)); }