diff --git a/youlai-auth/src/main/java/com/youlai/auth/config/DefaultSecurityConfig.java b/youlai-auth/src/main/java/com/youlai/auth/config/DefaultSecurityConfig.java index 21e557fb3..9144ea376 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/config/DefaultSecurityConfig.java +++ b/youlai-auth/src/main/java/com/youlai/auth/config/DefaultSecurityConfig.java @@ -1,5 +1,6 @@ package com.youlai.auth.config; +import cn.hutool.core.collection.CollectionUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -9,7 +10,14 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import java.util.Arrays; +import java.util.List; + +/** + * 默认安全配置 + */ @EnableWebSecurity @Configuration(proxyBeanMethods = false) public class DefaultSecurityConfig { @@ -18,14 +26,14 @@ public class DefaultSecurityConfig { * Spring Security 安全过滤器链配置 * * @param http 安全配置 - * @return + * @return 安全过滤器链 */ @Bean @Order(0) SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http - .authorizeHttpRequests(requestMatcherRegistry -> - requestMatcherRegistry.anyRequest().authenticated() + .authorizeHttpRequests(authorize -> + authorize.anyRequest().authenticated() ) .csrf(AbstractHttpConfigurer::disable) .formLogin(Customizer.withDefaults()); @@ -40,14 +48,32 @@ public class DefaultSecurityConfig { @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> - // 白名单②: 不走过滤器链(场景:静态资源js、css、html) + // 白名单: 不走过滤器链(场景:静态资源js、css、html) web.ignoring().requestMatchers( - "/webjars/**", - "/doc.html", - "/swagger-resources/**", - "/v3/api-docs/**", - "/swagger-ui/**" - ); + convertToAntPathRequestMatchers( + Arrays.asList("/webjars/**", + "/doc.html", + "/swagger-resources/**", + "/v3/api-docs/**", + "/swagger-ui/**") + )); + } + + + /** + * 将路径列表转换为AntPathRequestMatcher数组 + * + * @param paths 路径列表 + * @return AntPathRequestMatcher数组 + */ + public static AntPathRequestMatcher[] convertToAntPathRequestMatchers(List paths) { + if (CollectionUtil.isEmpty(paths)) { + return new AntPathRequestMatcher[0]; + } + + return paths.stream() + .map(AntPathRequestMatcher::new) + .toArray(AntPathRequestMatcher[]::new); } diff --git a/youlai-common/common-security/src/main/java/com/youlai/common/security/config/ResourceServerConfig.java b/youlai-common/common-security/src/main/java/com/youlai/common/security/config/ResourceServerConfig.java index 1d5f4b13d..7a4bda8be 100644 --- a/youlai-common/common-security/src/main/java/com/youlai/common/security/config/ResourceServerConfig.java +++ b/youlai-common/common-security/src/main/java/com/youlai/common/security/config/ResourceServerConfig.java @@ -1,9 +1,9 @@ package com.youlai.common.security.config; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.convert.Convert; import cn.hutool.json.JSONUtil; import com.youlai.common.constant.SecurityConstants; +import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.logging.log4j.util.Strings; @@ -20,41 +20,64 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import java.util.Arrays; import java.util.List; +/** + * 资源服务器配置 + * + * @author haoxr + * @since 3.0.0 + */ @ConfigurationProperties(prefix = "security") @Configuration @EnableWebSecurity +@RequiredArgsConstructor @Slf4j public class ResourceServerConfig { + private final AccessDeniedHandler accessDeniedHandler; + private final AuthenticationEntryPoint authenticationEntryPoint; + + + + /** + * 白名单路径列表 + */ @Setter - private List ignoreUrls; + private List whitelistPaths; + + @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { - log.info("whitelist path:{}", JSONUtil.toJsonStr(ignoreUrls)); - http.authorizeHttpRequests(requestMatcherRegistry -> + MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector); + + log.info("whitelist path:{}", JSONUtil.toJsonStr(whitelistPaths)); + http.authorizeHttpRequests((requests) -> { - if (CollectionUtil.isNotEmpty(ignoreUrls)) { - requestMatcherRegistry.requestMatchers(Convert.toStrArray(ignoreUrls)).permitAll(); + for (String whitelistPath : whitelistPaths) { + requests.requestMatchers(mvcMatcherBuilder.pattern(whitelistPath)).permitAll(); } - requestMatcherRegistry.anyRequest().authenticated(); + requests.anyRequest().authenticated(); } ) .csrf(AbstractHttpConfigurer::disable) ; http.oauth2ResourceServer(resourceServerConfigurer -> - resourceServerConfigurer.jwt(jwtConfigurer -> jwtAuthenticationConverter()) - ) - /*.and() - .authenticationEntryPoint(authenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler)*/ - ; + resourceServerConfigurer + .jwt(jwtConfigurer -> jwtAuthenticationConverter()) + .authenticationEntryPoint(authenticationEntryPoint) + .accessDeniedHandler(accessDeniedHandler) + ); return http.build(); } @@ -63,16 +86,39 @@ public class ResourceServerConfig { */ @Bean public WebSecurityCustomizer webSecurityCustomizer() { - return (web) -> web.ignoring() - .requestMatchers( - "/webjars/**", - "/doc.html", - "/swagger-resources/**", - "/v3/api-docs/**", - "/swagger-ui/**" - ); + return (web) -> web.ignoring().requestMatchers( + convertToAntPathRequestMatchers(Arrays.asList( + "/webjars/**", + "/doc.html", + "/swagger-resources/**", + "/v3/api-docs/**", + "/swagger-ui/**" + ) + ) + ); } + /** + * 路径转换为AntPathRequestMatcher + * + * @param paths 路径列表 + * @return AntPathRequestMatcher[] + */ + public static AntPathRequestMatcher[] convertToAntPathRequestMatchers(List paths) { + if (CollectionUtil.isEmpty(paths)) { + return new AntPathRequestMatcher[0]; + } + + return paths.stream() + .map(AntPathRequestMatcher::new) + .toArray(AntPathRequestMatcher[]::new); + } + + + @Bean + MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { + return new MvcRequestMatcher.Builder(introspector); + } /** * 自定义JWT Converter