From c41c06e93b779e94dc5e7f570545125d6c36bc83 Mon Sep 17 00:00:00 2001 From: fallrain Date: Mon, 12 May 2025 22:59:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9Anonymous=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0=E6=96=B9=E5=BC=8F,=E4=BD=BF?= =?UTF-8?q?=E7=94=A8security=205.5=E7=9A=84AuthorizationManager=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/config/SecurityConfig.java | 83 +++++++++---------- .../properties/PermitAllUrlProperties.java | 73 ---------------- .../AnonymousAuthorizationManager.java | 44 ++++++++++ 3 files changed, 82 insertions(+), 118 deletions(-) delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AnonymousAuthorizationManager.java diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index 18ac155bcc..32a32b5d04 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -1,5 +1,6 @@ package com.ruoyi.framework.config; +import com.ruoyi.framework.manager.AnonymousAuthorizationManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -16,26 +17,24 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.web.filter.CorsFilter; -import com.ruoyi.framework.config.properties.PermitAllUrlProperties; import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; /** * spring security配置 - * + * * @author ruoyi */ @EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) @Configuration -public class SecurityConfig -{ +public class SecurityConfig { /** * 自定义用户认证逻辑 */ @Autowired private UserDetailsService userDetailsService; - + /** * 认证失败处理类 */ @@ -53,25 +52,21 @@ public class SecurityConfig */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; - + /** * 跨域过滤器 */ @Autowired private CorsFilter corsFilter; - /** - * 允许匿名访问的地址 - */ @Autowired - private PermitAllUrlProperties permitAllUrl; + private AnonymousAuthorizationManager anonymousAuthorizationManager; /** * 身份验证实现 */ @Bean - public AuthenticationManager authenticationManager() - { + public AuthenticationManager authenticationManager() { DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); daoAuthenticationProvider.setUserDetailsService(userDetailsService); daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder()); @@ -94,46 +89,44 @@ public class SecurityConfig * authenticated | 用户登录后可访问 */ @Bean - protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception - { + protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity - // CSRF禁用,因为不使用session - .csrf(csrf -> csrf.disable()) - // 禁用HTTP响应标头 - .headers((headersCustomizer) -> { - headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); - }) - // 认证失败处理类 - .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) - // 基于token,所以不需要session - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - // 注解标记允许匿名访问的url - .authorizeHttpRequests((requests) -> { - permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll()); - // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - requests.requestMatchers("/login", "/register", "/captchaImage").permitAll() - // 静态资源,可匿名访问 - .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll() - .requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll() - // 除上面外的所有请求全部需要鉴权认证 - .anyRequest().authenticated(); - }) - // 添加Logout filter - .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) - // 添加JWT filter - .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) - // 添加CORS filter - .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) - .addFilterBefore(corsFilter, LogoutFilter.class) - .build(); + // CSRF禁用,因为不使用session + .csrf(csrf -> csrf.disable()) + // 禁用HTTP响应标头 + .headers((headersCustomizer) -> { + headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); + }) + // 认证失败处理类 + .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) + // 基于token,所以不需要session + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // 注解标记允许匿名访问的url + .authorizeHttpRequests((requests) -> { +// permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll()); + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + requests.requestMatchers("/login", "/register", "/captchaImage").permitAll() + // 静态资源,可匿名访问 + .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll() + .requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().access(anonymousAuthorizationManager); + }) + // 添加Logout filter + .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) + // 添加JWT filter + .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) + // 添加CORS filter + .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) + .addFilterBefore(corsFilter, LogoutFilter.class) + .build(); } /** * 强散列哈希加密实现 */ @Bean - public BCryptPasswordEncoder bCryptPasswordEncoder() - { + public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java deleted file mode 100644 index 7f474f1995..0000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.ruoyi.framework.config.properties; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.regex.Pattern; -import org.apache.commons.lang3.RegExUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import com.ruoyi.common.annotation.Anonymous; - -/** - * 设置Anonymous注解允许匿名访问的url - * - * @author ruoyi - */ -@Configuration -public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware -{ - private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); - - private ApplicationContext applicationContext; - - private List urls = new ArrayList<>(); - - public String ASTERISK = "*"; - - @Override - public void afterPropertiesSet() - { - RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); - Map map = mapping.getHandlerMethods(); - - map.keySet().forEach(info -> { - HandlerMethod handlerMethod = map.get(info); - - // 获取方法上边的注解 替代path variable 为 * - Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); - Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) // - .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); - - // 获取类上边的注解, 替代path variable 为 * - Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); - Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) - .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); - }); - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException - { - this.applicationContext = context; - } - - public List getUrls() - { - return urls; - } - - public void setUrls(List urls) - { - this.urls = urls; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AnonymousAuthorizationManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AnonymousAuthorizationManager.java new file mode 100644 index 0000000000..7554642a47 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AnonymousAuthorizationManager.java @@ -0,0 +1,44 @@ +package com.ruoyi.framework.manager; + +import com.ruoyi.common.annotation.Anonymous; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.Objects; +import java.util.function.Supplier; + +@Component +public class AnonymousAuthorizationManager implements AuthorizationManager { + private final RequestMappingHandlerMapping handlerMapping; + + public AnonymousAuthorizationManager(RequestMappingHandlerMapping handlerMapping) { + this.handlerMapping = handlerMapping; + } + + @Override + public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext context) { + try { + Object handler = handlerMapping.getHandler(context.getRequest()).getHandler(); + if (handler instanceof HandlerMethod handlerMethod) { + if(Objects.nonNull( AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class))) { + return new AuthorizationDecision(true); + } + if (Objects.nonNull(AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class))) { + return new AuthorizationDecision(true); + } + } + } catch (Exception e) { + } + Authentication auth = authentication.get(); + if (Objects.isNull(auth) || !auth.isAuthenticated() || Objects.equals(auth.getName(), "anonymousUser")) { + return new AuthorizationDecision(false); + } + return new AuthorizationDecision(true); + } +} -- Gitee