diff --git a/src/main/java/com/cool/core/annotation/TokenIgnore.java b/src/main/java/com/cool/core/annotation/TokenIgnore.java index 1392043..b2d46aa 100644 --- a/src/main/java/com/cool/core/annotation/TokenIgnore.java +++ b/src/main/java/com/cool/core/annotation/TokenIgnore.java @@ -11,4 +11,5 @@ import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface TokenIgnore { + String[] value() default {}; } diff --git a/src/main/java/com/cool/core/security/JwtSecurityConfig.java b/src/main/java/com/cool/core/security/JwtSecurityConfig.java index 9ccd4c9..13480d7 100644 --- a/src/main/java/com/cool/core/security/JwtSecurityConfig.java +++ b/src/main/java/com/cool/core/security/JwtSecurityConfig.java @@ -1,6 +1,5 @@ package com.cool.core.security; -import com.cool.core.annotation.CoolRestController; import com.cool.core.annotation.TokenIgnore; import com.cool.modules.base.security.JwtAuthenticationTokenFilter; import lombok.RequiredArgsConstructor; @@ -22,7 +21,6 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.util.DigestUtils; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.context.ApplicationContext; import org.springframework.web.method.HandlerMethod; @@ -31,8 +29,9 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl import org.springframework.web.util.pattern.PathPattern; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import java.util.Map; -import java.util.function.Predicate; @EnableWebSecurity @Configuration @@ -40,92 +39,111 @@ import java.util.function.Predicate; @RequiredArgsConstructor public class JwtSecurityConfig { - // 用户详情 - final private UserDetailsService userDetailsService; - final private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; - // 401 - final private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler; - // 403 - final private RestAccessDeniedHandler restAccessDeniedHandler; - // 忽略权限控制的地址 - final private IgnoredUrlsProperties ignoredUrlsProperties; + // 用户详情 + final private UserDetailsService userDetailsService; + final private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + // 401 + final private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler; + // 403 + final private RestAccessDeniedHandler restAccessDeniedHandler; + // 忽略权限控制的地址 + final private IgnoredUrlsProperties ignoredUrlsProperties; - final private RequestMappingHandlerMapping requestMappingHandlerMapping; + final private RequestMappingHandlerMapping requestMappingHandlerMapping; - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, ApplicationContext applicationContext) throws Exception { - // 动态获取忽略的URL - configureIgnoredUrls(); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, ApplicationContext applicationContext) throws Exception { + // 动态获取忽略的URL + configureIgnoredUrls(); - return httpSecurity - .authorizeHttpRequests( - conf -> conf.requestMatchers( - ignoredUrlsProperties.getUrls().toArray(String[]::new)) - .permitAll().anyRequest().authenticated()) - .headers(config -> config.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) - // 允许网页iframe - .csrf(AbstractHttpConfigurer::disable) - .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .addFilterBefore(jwtAuthenticationTokenFilter, - UsernamePasswordAuthenticationFilter.class) - .exceptionHandling(config -> { - config.authenticationEntryPoint(entryPointUnauthorizedHandler); - config.accessDeniedHandler(restAccessDeniedHandler); - }).build(); - } + return httpSecurity + .authorizeHttpRequests( + conf -> { + conf.requestMatchers( + ignoredUrlsProperties.getUrls().toArray(String[]::new)) + .permitAll(); + conf.requestMatchers("/admin/**").authenticated(); + }) + .headers(config -> config.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) + // 允许网页iframe + .csrf(AbstractHttpConfigurer::disable) + .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .addFilterBefore(jwtAuthenticationTokenFilter, + UsernamePasswordAuthenticationFilter.class) + .exceptionHandling(config -> { + config.authenticationEntryPoint(entryPointUnauthorizedHandler); + config.accessDeniedHandler(restAccessDeniedHandler); + }).build(); + } - private void configureIgnoredUrls() { - Map mappings = requestMappingHandlerMapping.getHandlerMethods(); - mappings.forEach((requestMappingInfo, handlerMethod) -> { - Method method = handlerMethod.getMethod(); - TokenIgnore tokenIgnore = AnnotatedElementUtils.findMergedAnnotation(method, TokenIgnore.class); - if (tokenIgnore != null) { - StringBuilder url = new StringBuilder(); - RequestMapping classRequestMapping = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), RequestMapping.class); - if (classRequestMapping != null) { - for (String path : classRequestMapping.value()) { - url.append(path); - } - } - if (requestMappingInfo.getPathPatternsCondition() == null) { - return; - } - // requestMappingInfo.getPathPatternsCondition().getPatterns() - for (PathPattern path : requestMappingInfo.getPathPatternsCondition().getPatterns()) { - url.append(path); - } - ignoredUrlsProperties.getUrls().add(url.toString()); - } - }); - } + private void configureIgnoredUrls() { + Map mappings = requestMappingHandlerMapping.getHandlerMethods(); + List handlerCtr = new ArrayList<>(); + mappings.forEach((requestMappingInfo, handlerMethod) -> { + Method method = handlerMethod.getMethod(); + TokenIgnore tokenIgnore = AnnotatedElementUtils.findMergedAnnotation(method, TokenIgnore.class); + TokenIgnore tokenIgnoreCtr = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), TokenIgnore.class); + if (!handlerCtr.contains(handlerMethod.getBeanType().getName()) && tokenIgnoreCtr != null) { + requestMappingInfo.getPathPatternsCondition().getPatterns().forEach(pathPattern -> { + String[] prefixs = pathPattern.getPatternString().split("/"); + // 去除最后一个路径 + List urls = new ArrayList<>(); + for (int i = 0; i < prefixs.length - 1; i++) { + urls.add(prefixs[i]); + } + // 遍历 tokenIgnoreCtr.value() + for (String path : tokenIgnoreCtr.value()) { + ignoredUrlsProperties.getUrls().add(String.join("/", urls) + "/" + path); + } + handlerCtr.add(handlerMethod.getBeanType().getName()); + }); + } + if (tokenIgnore != null) { + StringBuilder url = new StringBuilder(); + RequestMapping classRequestMapping = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), RequestMapping.class); + if (classRequestMapping != null) { + for (String path : classRequestMapping.value()) { + url.append(path); + } + } + if (requestMappingInfo.getPathPatternsCondition() == null) { + return; + } + for (PathPattern path : requestMappingInfo.getPathPatternsCondition().getPatterns()) { + url.append(path); + } + ignoredUrlsProperties.getUrls().add(url.toString()); + } + }); + } - @Bean - public PasswordEncoder passwordEncoder() { - return new PasswordEncoder() { - @Override - public String encode(CharSequence rawPassword) { - return DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes()); - } + @Bean + public PasswordEncoder passwordEncoder() { + return new PasswordEncoder() { + @Override + public String encode(CharSequence rawPassword) { + return DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes()); + } - @Override - public boolean matches(CharSequence rawPassword, String encodedPassword) { - return encodedPassword.equals( - DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes())); - } - }; - } + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + return encodedPassword.equals( + DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes())); + } + }; + } - @Bean - public AuthenticationProvider authenticationProvider() { - DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); - authProvider.setUserDetailsService(userDetailsService); - authProvider.setPasswordEncoder(passwordEncoder()); - return authProvider; - } + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration config) - throws Exception { - return config.getAuthenticationManager(); - } + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) + throws Exception { + return config.getAuthenticationManager(); + } } diff --git a/src/main/java/com/cool/core/security/MyAccessDecisionManager.java b/src/main/java/com/cool/core/security/MyAccessDecisionManager.java index 6542033..03f9ef9 100644 --- a/src/main/java/com/cool/core/security/MyAccessDecisionManager.java +++ b/src/main/java/com/cool/core/security/MyAccessDecisionManager.java @@ -1,5 +1,6 @@ package com.cool.core.security; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; @@ -7,23 +8,34 @@ import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.FilterInvocation; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Iterator; +import java.util.List; /** * 权限管理决断器 判断用户拥有的权限或角色是否有资源访问权限 */ +@RequiredArgsConstructor @Slf4j @Component public class MyAccessDecisionManager implements AccessDecisionManager { + // 忽略权限控制的地址 + final private IgnoredUrlsProperties ignoredUrlsProperties; + @Override public void decide(Authentication authentication, Object o, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (configAttributes == null) { return; } + List urls = ignoredUrlsProperties.getUrls(); + String url = ((FilterInvocation) o).getRequestUrl().split("[?]")[0]; + if (urls.contains(url)) { + return; + } Iterator iterator = configAttributes.iterator(); while (iterator.hasNext()) { ConfigAttribute c = iterator.next(); @@ -47,4 +59,4 @@ public class MyAccessDecisionManager implements AccessDecisionManager { public boolean supports(Class aClass) { return true; } -} \ No newline at end of file +} diff --git a/src/main/java/com/cool/modules/dict/controller/admin/AdminDictInfoController.java b/src/main/java/com/cool/modules/dict/controller/admin/AdminDictInfoController.java index 6ad3483..5fd055a 100644 --- a/src/main/java/com/cool/modules/dict/controller/admin/AdminDictInfoController.java +++ b/src/main/java/com/cool/modules/dict/controller/admin/AdminDictInfoController.java @@ -11,16 +11,15 @@ import com.cool.modules.dict.service.DictInfoService; import com.mybatisflex.core.query.QueryWrapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import jakarta.servlet.http.HttpServletRequest; - /** * 字典信息 */ @Tag(name = "字典信息", description = "字典信息") -@CoolRestController(api = { "add", "delete", "update", "page", "list", "info" }) +@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"}) public class AdminDictInfoController extends BaseController { @Override protected void init(HttpServletRequest request, JSONObject requestParams) { @@ -34,4 +33,4 @@ public class AdminDictInfoController extends BaseController