mirror of https://github.com/jeecgboot/jeecg-boot
修复#6100、@IgnoreAuth扫描加速
parent
f1496b5084
commit
faebdee755
|
@ -27,6 +27,7 @@ import org.springframework.core.env.Environment;
|
|||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
|
@ -48,7 +49,7 @@ import java.util.*;
|
|||
@Slf4j
|
||||
@Configuration
|
||||
// 免认证注解 @IgnoreAuth 注解生效范围配置
|
||||
@ComponentScan(basePackages = {"org.jeecg.**.controller"})
|
||||
@ComponentScan(basePackages = {"org.jeecg.**"})
|
||||
public class ShiroConfig {
|
||||
|
||||
@Resource
|
||||
|
@ -173,16 +174,6 @@ public class ShiroConfig {
|
|||
// 企业微信证书排除
|
||||
filterChainDefinitionMap.put("/WW_verify*", "anon");
|
||||
|
||||
|
||||
// 通过注解免登录url
|
||||
List<String> ignoreAuthUrlList = collectIgnoreAuthUrl();
|
||||
if (!CollectionUtils.isEmpty(ignoreAuthUrlList)) {
|
||||
for (String url : ignoreAuthUrlList) {
|
||||
filterChainDefinitionMap.put(url, "anon");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 添加自己的过滤器并且取名为jwt
|
||||
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
||||
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
|
||||
|
@ -335,74 +326,4 @@ public class ShiroConfig {
|
|||
return manager;
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public List<String> collectIgnoreAuthUrl() {
|
||||
List<String> ignoreAuthUrls = new ArrayList<>();
|
||||
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
|
||||
provider.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
|
||||
|
||||
// 获取当前类的扫描注解的配置
|
||||
Set<BeanDefinition> components = new HashSet<>();
|
||||
for (String basePackage : AnnotationUtils.getAnnotation(ShiroConfig.class, ComponentScan.class).basePackages()) {
|
||||
components.addAll(provider.findCandidateComponents(basePackage));
|
||||
}
|
||||
|
||||
// 逐个匹配获取免认证路径
|
||||
for (BeanDefinition component : components) {
|
||||
String beanClassName = component.getBeanClassName();
|
||||
Class<?> clazz = Class.forName(beanClassName);
|
||||
RequestMapping base = clazz.getAnnotation(RequestMapping.class);
|
||||
String[] baseUrl = {};
|
||||
if (Objects.nonNull(base)) {
|
||||
baseUrl = base.value();
|
||||
}
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(RequestMapping.class)) {
|
||||
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(GetMapping.class)) {
|
||||
GetMapping requestMapping = method.getAnnotation(GetMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PostMapping.class)) {
|
||||
PostMapping requestMapping = method.getAnnotation(PostMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PutMapping.class)) {
|
||||
PutMapping requestMapping = method.getAnnotation(PutMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(DeleteMapping.class)) {
|
||||
DeleteMapping requestMapping = method.getAnnotation(DeleteMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PatchMapping.class)) {
|
||||
PatchMapping requestMapping = method.getAnnotation(PatchMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ignoreAuthUrls;
|
||||
}
|
||||
|
||||
private List<String> rebuildUrl(String[] bases, String[] uris) {
|
||||
List<String> urls = new ArrayList<>();
|
||||
for (String base : bases) {
|
||||
for (String uri : uris) {
|
||||
urls.add(prefix(base)+prefix(uri));
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
private String prefix(String seg) {
|
||||
return seg.startsWith("/") ? seg : "/"+seg;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.jeecg.common.constant.CommonConstant;
|
|||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.shiro.JwtToken;
|
||||
import org.jeecg.config.shiro.ignore.InMemoryIgnoreAuth;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
@ -47,6 +48,10 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
|
|||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
try {
|
||||
// 判断当前路径是不是注解了@IngoreAuth路径,如果是,则放开验证
|
||||
if (InMemoryIgnoreAuth.contains(((HttpServletRequest) request).getServletPath())) {
|
||||
return true;
|
||||
}
|
||||
executeLogin(request, response);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package org.jeecg.config.shiro.ignore;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.jeecg.config.shiro.IgnoreAuth;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 在spring boot初始化时,根据@RestController注解获取当前spring容器中的bean
|
||||
* @author eightmonth@qq.com
|
||||
* @date 2024/4/18 11:35
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class IgnoreAuthPostProcessor implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
List<String> ignoreAuthUrls = new ArrayList<>();
|
||||
if (event.getApplicationContext().getParent() == null) {
|
||||
// 只处理根应用上下文的事件,避免在子上下文中重复处理
|
||||
Map<String, Object> restControllers = applicationContext.getBeansWithAnnotation(RestController.class);
|
||||
for (Object restController : restControllers.values()) {
|
||||
// 如 online系统的controller并不是spring 默认生成
|
||||
if (restController instanceof Advised) {
|
||||
ignoreAuthUrls.addAll(postProcessRestController(restController));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(ignoreAuthUrls)) {
|
||||
InMemoryIgnoreAuth.set(ignoreAuthUrls);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> postProcessRestController(Object restController) {
|
||||
List<String> ignoreAuthUrls = new ArrayList<>();
|
||||
Class<?> clazz = ((Advised) restController).getTargetClass();
|
||||
RequestMapping base = clazz.getAnnotation(RequestMapping.class);
|
||||
String[] baseUrl = Objects.nonNull(base) ? base.value() : new String[]{};
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(RequestMapping.class)) {
|
||||
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(GetMapping.class)) {
|
||||
GetMapping requestMapping = method.getAnnotation(GetMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PostMapping.class)) {
|
||||
PostMapping requestMapping = method.getAnnotation(PostMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PutMapping.class)) {
|
||||
PutMapping requestMapping = method.getAnnotation(PutMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(DeleteMapping.class)) {
|
||||
DeleteMapping requestMapping = method.getAnnotation(DeleteMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PatchMapping.class)) {
|
||||
PatchMapping requestMapping = method.getAnnotation(PatchMapping.class);
|
||||
String[] uri = requestMapping.value();
|
||||
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
|
||||
}
|
||||
}
|
||||
|
||||
return ignoreAuthUrls;
|
||||
}
|
||||
|
||||
private List<String> rebuildUrl(String[] bases, String[] uris) {
|
||||
List<String> urls = new ArrayList<>();
|
||||
if (bases.length > 0) {
|
||||
for (String base : bases) {
|
||||
for (String uri : uris) {
|
||||
urls.add(prefix(base) + prefix(uri));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Arrays.stream(uris).forEach(uri -> {
|
||||
urls.add(prefix(uri));
|
||||
});
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
private String prefix(String seg) {
|
||||
return seg.startsWith("/") ? seg : "/"+seg;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package org.jeecg.config.shiro.ignore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 使用内存存储通过@IgnoreAuth注解的url,配合JwtFilter进行免登录校验
|
||||
* PS:无法使用ThreadLocal进行存储,因为ThreadLocal装载时,JwtFilter已经初始化完毕,导致该类获取ThreadLocal为空
|
||||
* @author eightmonth@qq.com
|
||||
* @date 2024/4/18 15:02
|
||||
*/
|
||||
public class InMemoryIgnoreAuth {
|
||||
private static final List<String> IGNORE_AUTH_LIST = new ArrayList<>();
|
||||
|
||||
public InMemoryIgnoreAuth() {}
|
||||
|
||||
public static void set(List<String> list) {
|
||||
IGNORE_AUTH_LIST.addAll(list);
|
||||
}
|
||||
|
||||
public static List<String> get() {
|
||||
return IGNORE_AUTH_LIST;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
IGNORE_AUTH_LIST.clear();
|
||||
}
|
||||
|
||||
public static boolean contains(String url) {
|
||||
for (String ignoreAuth : IGNORE_AUTH_LIST) {
|
||||
if (url.endsWith(ignoreAuth)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package org.jeecg;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.shiro.ignore.InMemoryIgnoreAuth;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package org.jeecg.handler.swagger;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import springfox.documentation.swagger.web.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* swagger聚合接口,三个接口都是 doc.html需要访问的接口
|
||||
|
@ -19,6 +22,11 @@ import java.util.List;
|
|||
public class SwaggerResourceController {
|
||||
private MySwaggerResourceProvider swaggerResourceProvider;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
// 生产环境profile配置模型
|
||||
private static final String PRODUCTION_PROFILE_NAME = "prod*";
|
||||
|
||||
@Autowired
|
||||
public SwaggerResourceController(MySwaggerResourceProvider swaggerResourceProvider) {
|
||||
this.swaggerResourceProvider = swaggerResourceProvider;
|
||||
|
@ -36,6 +44,22 @@ public class SwaggerResourceController {
|
|||
|
||||
@RequestMapping
|
||||
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
|
||||
// 如果激活的profile带有生产环境的profile,则屏蔽swagger资源
|
||||
if (isProd()) {
|
||||
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
|
||||
}
|
||||
return new ResponseEntity<>(swaggerResourceProvider.get(), HttpStatus.OK);
|
||||
}
|
||||
|
||||
private boolean isProd() {
|
||||
String[] profiles = applicationContext.getEnvironment().getActiveProfiles();
|
||||
Pattern pattern = Pattern.compile(PRODUCTION_PROFILE_NAME);
|
||||
for (String profile : profiles) {
|
||||
if (pattern.matcher(profile).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import org.springframework.core.env.Environment;
|
|||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
|
@ -35,6 +36,7 @@ public class JeecgSystemCloudApplication extends SpringBootServletInitializer im
|
|||
private RedisTemplate<String, Object> redisTemplate;
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||
|
||||
return application.sources(JeecgSystemCloudApplication.class);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue