Skip to content

Commit

Permalink
修复jeecgboot#6100、@IgnoreAuth扫描加速
Browse files Browse the repository at this point in the history
  • Loading branch information
EightMonth committed Apr 25, 2024
1 parent f1496b5 commit faebdee
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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;
Expand All @@ -48,7 +49,7 @@
@Slf4j
@Configuration
// 免认证注解 @IgnoreAuth 注解生效范围配置
@ComponentScan(basePackages = {"org.jeecg.**.controller"})
@ComponentScan(basePackages = {"org.jeecg.**"})
public class ShiroConfig {

@Resource
Expand Down Expand Up @@ -173,16 +174,6 @@ public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
// 企业微信证书排除
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为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
Expand Down Expand Up @@ -335,74 +326,4 @@ public IRedisManager redisManager() {
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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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;
Expand Down Expand Up @@ -47,6 +48,10 @@ public JwtFilter(boolean allowOrigin){
@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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 [email protected]
* @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;
}
}
Original file line number Diff line number Diff line change
@@ -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 [email protected]
* @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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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需要访问的接口
Expand All @@ -19,6 +22,11 @@
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;
Expand All @@ -36,6 +44,22 @@ public ResponseEntity<UiConfiguration> uiConfiguration() {

@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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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;

Expand All @@ -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);
}

Expand Down

0 comments on commit faebdee

Please sign in to comment.