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.core.type.filter.AnnotationTypeFilter;
|
||||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.util.StopWatch;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||||
|
@ -48,7 +49,7 @@ import java.util.*;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
// 免认证注解 @IgnoreAuth 注解生效范围配置
|
// 免认证注解 @IgnoreAuth 注解生效范围配置
|
||||||
@ComponentScan(basePackages = {"org.jeecg.**.controller"})
|
@ComponentScan(basePackages = {"org.jeecg.**"})
|
||||||
public class ShiroConfig {
|
public class ShiroConfig {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -173,16 +174,6 @@ public class ShiroConfig {
|
||||||
// 企业微信证书排除
|
// 企业微信证书排除
|
||||||
filterChainDefinitionMap.put("/WW_verify*", "anon");
|
filterChainDefinitionMap.put("/WW_verify*", "anon");
|
||||||
|
|
||||||
|
|
||||||
// 通过注解免登录url
|
|
||||||
List<String> ignoreAuthUrlList = collectIgnoreAuthUrl();
|
|
||||||
if (!CollectionUtils.isEmpty(ignoreAuthUrlList)) {
|
|
||||||
for (String url : ignoreAuthUrlList) {
|
|
||||||
filterChainDefinitionMap.put(url, "anon");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 添加自己的过滤器并且取名为jwt
|
// 添加自己的过滤器并且取名为jwt
|
||||||
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
||||||
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
|
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
|
||||||
|
@ -335,74 +326,4 @@ public class ShiroConfig {
|
||||||
return manager;
|
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.system.util.JwtUtil;
|
||||||
import org.jeecg.common.util.oConvertUtils;
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
import org.jeecg.config.shiro.JwtToken;
|
import org.jeecg.config.shiro.JwtToken;
|
||||||
|
import org.jeecg.config.shiro.ignore.InMemoryIgnoreAuth;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
@ -47,6 +48,10 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||||
@Override
|
@Override
|
||||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||||
try {
|
try {
|
||||||
|
// 判断当前路径是不是注解了@IngoreAuth路径,如果是,则放开验证
|
||||||
|
if (InMemoryIgnoreAuth.contains(((HttpServletRequest) request).getServletPath())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
executeLogin(request, response);
|
executeLogin(request, response);
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} 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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.jeecg.common.util.oConvertUtils;
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
|
import org.jeecg.config.shiro.ignore.InMemoryIgnoreAuth;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
package org.jeecg.handler.swagger;
|
package org.jeecg.handler.swagger;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import springfox.documentation.swagger.web.*;
|
import springfox.documentation.swagger.web.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* swagger聚合接口,三个接口都是 doc.html需要访问的接口
|
* swagger聚合接口,三个接口都是 doc.html需要访问的接口
|
||||||
|
@ -19,6 +22,11 @@ import java.util.List;
|
||||||
public class SwaggerResourceController {
|
public class SwaggerResourceController {
|
||||||
private MySwaggerResourceProvider swaggerResourceProvider;
|
private MySwaggerResourceProvider swaggerResourceProvider;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
// 生产环境profile配置模型
|
||||||
|
private static final String PRODUCTION_PROFILE_NAME = "prod*";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SwaggerResourceController(MySwaggerResourceProvider swaggerResourceProvider) {
|
public SwaggerResourceController(MySwaggerResourceProvider swaggerResourceProvider) {
|
||||||
this.swaggerResourceProvider = swaggerResourceProvider;
|
this.swaggerResourceProvider = swaggerResourceProvider;
|
||||||
|
@ -36,6 +44,22 @@ public class SwaggerResourceController {
|
||||||
|
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
|
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
|
||||||
|
// 如果激活的profile带有生产环境的profile,则屏蔽swagger资源
|
||||||
|
if (isProd()) {
|
||||||
|
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
|
||||||
|
}
|
||||||
return new ResponseEntity<>(swaggerResourceProvider.get(), 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.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ public class JeecgSystemCloudApplication extends SpringBootServletInitializer im
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
@Override
|
@Override
|
||||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||||
|
|
||||||
return application.sources(JeecgSystemCloudApplication.class);
|
return application.sources(JeecgSystemCloudApplication.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue