完善TokenIgnore,支持Controller

This commit is contained in:
COOL
2024-07-24 17:01:02 +08:00
parent 12ac71e93f
commit ba400993dc
5 changed files with 119 additions and 89 deletions

View File

@@ -11,4 +11,5 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD}) @Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface TokenIgnore { public @interface TokenIgnore {
String[] value() default {};
} }

View File

@@ -1,6 +1,5 @@
package com.cool.core.security; package com.cool.core.security;
import com.cool.core.annotation.CoolRestController;
import com.cool.core.annotation.TokenIgnore; import com.cool.core.annotation.TokenIgnore;
import com.cool.modules.base.security.JwtAuthenticationTokenFilter; import com.cool.modules.base.security.JwtAuthenticationTokenFilter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -22,7 +21,6 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.DigestUtils; import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.web.method.HandlerMethod; 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 org.springframework.web.util.pattern.PathPattern;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
@EnableWebSecurity @EnableWebSecurity
@Configuration @Configuration
@@ -40,92 +39,111 @@ import java.util.function.Predicate;
@RequiredArgsConstructor @RequiredArgsConstructor
public class JwtSecurityConfig { public class JwtSecurityConfig {
// 用户详情 // 用户详情
final private UserDetailsService userDetailsService; final private UserDetailsService userDetailsService;
final private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; final private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
// 401 // 401
final private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler; final private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;
// 403 // 403
final private RestAccessDeniedHandler restAccessDeniedHandler; final private RestAccessDeniedHandler restAccessDeniedHandler;
// 忽略权限控制的地址 // 忽略权限控制的地址
final private IgnoredUrlsProperties ignoredUrlsProperties; final private IgnoredUrlsProperties ignoredUrlsProperties;
final private RequestMappingHandlerMapping requestMappingHandlerMapping; final private RequestMappingHandlerMapping requestMappingHandlerMapping;
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, ApplicationContext applicationContext) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, ApplicationContext applicationContext) throws Exception {
// 动态获取忽略的URL // 动态获取忽略的URL
configureIgnoredUrls(); configureIgnoredUrls();
return httpSecurity return httpSecurity
.authorizeHttpRequests( .authorizeHttpRequests(
conf -> conf.requestMatchers( conf -> {
ignoredUrlsProperties.getUrls().toArray(String[]::new)) conf.requestMatchers(
.permitAll().anyRequest().authenticated()) ignoredUrlsProperties.getUrls().toArray(String[]::new))
.headers(config -> config.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .permitAll();
// 允许网页iframe conf.requestMatchers("/admin/**").authenticated();
.csrf(AbstractHttpConfigurer::disable) })
.sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .headers(config -> config.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.addFilterBefore(jwtAuthenticationTokenFilter, // 允许网页iframe
UsernamePasswordAuthenticationFilter.class) .csrf(AbstractHttpConfigurer::disable)
.exceptionHandling(config -> { .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
config.authenticationEntryPoint(entryPointUnauthorizedHandler); .addFilterBefore(jwtAuthenticationTokenFilter,
config.accessDeniedHandler(restAccessDeniedHandler); UsernamePasswordAuthenticationFilter.class)
}).build(); .exceptionHandling(config -> {
} config.authenticationEntryPoint(entryPointUnauthorizedHandler);
config.accessDeniedHandler(restAccessDeniedHandler);
}).build();
}
private void configureIgnoredUrls() { private void configureIgnoredUrls() {
Map<RequestMappingInfo, HandlerMethod> mappings = requestMappingHandlerMapping.getHandlerMethods(); Map<RequestMappingInfo, HandlerMethod> mappings = requestMappingHandlerMapping.getHandlerMethods();
mappings.forEach((requestMappingInfo, handlerMethod) -> { List<String> handlerCtr = new ArrayList<>();
Method method = handlerMethod.getMethod(); mappings.forEach((requestMappingInfo, handlerMethod) -> {
TokenIgnore tokenIgnore = AnnotatedElementUtils.findMergedAnnotation(method, TokenIgnore.class); Method method = handlerMethod.getMethod();
if (tokenIgnore != null) { TokenIgnore tokenIgnore = AnnotatedElementUtils.findMergedAnnotation(method, TokenIgnore.class);
StringBuilder url = new StringBuilder(); TokenIgnore tokenIgnoreCtr = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), TokenIgnore.class);
RequestMapping classRequestMapping = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), RequestMapping.class); if (!handlerCtr.contains(handlerMethod.getBeanType().getName()) && tokenIgnoreCtr != null) {
if (classRequestMapping != null) { requestMappingInfo.getPathPatternsCondition().getPatterns().forEach(pathPattern -> {
for (String path : classRequestMapping.value()) { String[] prefixs = pathPattern.getPatternString().split("/");
url.append(path); // 去除最后一个路径
} List<String> urls = new ArrayList<>();
} for (int i = 0; i < prefixs.length - 1; i++) {
if (requestMappingInfo.getPathPatternsCondition() == null) { urls.add(prefixs[i]);
return; }
} // 遍历 tokenIgnoreCtr.value()
// requestMappingInfo.getPathPatternsCondition().getPatterns() for (String path : tokenIgnoreCtr.value()) {
for (PathPattern path : requestMappingInfo.getPathPatternsCondition().getPatterns()) { ignoredUrlsProperties.getUrls().add(String.join("/", urls) + "/" + path);
url.append(path); }
} handlerCtr.add(handlerMethod.getBeanType().getName());
ignoredUrlsProperties.getUrls().add(url.toString()); });
} }
}); 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 @Bean
public PasswordEncoder passwordEncoder() { public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() { return new PasswordEncoder() {
@Override @Override
public String encode(CharSequence rawPassword) { public String encode(CharSequence rawPassword) {
return DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes()); return DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes());
} }
@Override @Override
public boolean matches(CharSequence rawPassword, String encodedPassword) { public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals( return encodedPassword.equals(
DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes())); DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes()));
} }
}; };
} }
@Bean @Bean
public AuthenticationProvider authenticationProvider() { public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService); authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder()); authProvider.setPasswordEncoder(passwordEncoder());
return authProvider; return authProvider;
} }
@Bean @Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) public AuthenticationManager authenticationManager(AuthenticationConfiguration config)
throws Exception { throws Exception {
return config.getAuthenticationManager(); return config.getAuthenticationManager();
} }
} }

View File

@@ -1,5 +1,6 @@
package com.cool.core.security; package com.cool.core.security;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException; 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.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
/** /**
* 权限管理决断器 判断用户拥有的权限或角色是否有资源访问权限 * 权限管理决断器 判断用户拥有的权限或角色是否有资源访问权限
*/ */
@RequiredArgsConstructor
@Slf4j @Slf4j
@Component @Component
public class MyAccessDecisionManager implements AccessDecisionManager { public class MyAccessDecisionManager implements AccessDecisionManager {
// 忽略权限控制的地址
final private IgnoredUrlsProperties ignoredUrlsProperties;
@Override @Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException { throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null) { if (configAttributes == null) {
return; return;
} }
List<String> urls = ignoredUrlsProperties.getUrls();
String url = ((FilterInvocation) o).getRequestUrl().split("[?]")[0];
if (urls.contains(url)) {
return;
}
Iterator<ConfigAttribute> iterator = configAttributes.iterator(); Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
ConfigAttribute c = iterator.next(); ConfigAttribute c = iterator.next();

View File

@@ -11,16 +11,15 @@ import com.cool.modules.dict.service.DictInfoService;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; 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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import jakarta.servlet.http.HttpServletRequest;
/** /**
* 字典信息 * 字典信息
*/ */
@Tag(name = "字典信息", description = "字典信息") @Tag(name = "字典信息", description = "字典信息")
@CoolRestController(api = { "add", "delete", "update", "page", "list", "info" }) @CoolRestController(api = {"add", "delete", "update", "page", "list", "info"})
public class AdminDictInfoController extends BaseController<DictInfoService, DictInfoEntity> { public class AdminDictInfoController extends BaseController<DictInfoService, DictInfoEntity> {
@Override @Override
protected void init(HttpServletRequest request, JSONObject requestParams) { protected void init(HttpServletRequest request, JSONObject requestParams) {