policeSecurity/policeSecurityServer/src/main/java/com/changhu/config/WebConfig.java

177 lines
7.7 KiB
Java

package com.changhu.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import com.changhu.common.annotation.IsWhiteList;
import com.changhu.common.annotation.JsonBody;
import com.changhu.filter.BodyWrapperFilter;
import com.changhu.interceptor.JsonBodyInterceptor;
import com.changhu.interceptor.OpenApiAuthInterceptor;
import com.changhu.interceptor.UserTypeInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
/**
* author: luozhun
* desc: WebConfig
* createTime: 2023/8/18 10:56
*/
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final List<String> whiteList = new ArrayList<>();
/**
* 扫描所有Controller
*/
private static Set<Class<?>> scanRestController() {
//需要过滤出JsonBody
Set<Class<?>> classes = ClassUtil.scanPackage("com.changhu", clazz -> !JsonBody.class.equals(clazz));
return classes.stream().filter(aClass -> {
//判断类上是否有Controller注解
if (aClass.isAnnotationPresent(Controller.class) || aClass.isAnnotationPresent(RestController.class)) {
return true;
}
Annotation[] annotations = aClass.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(Controller.class) || annotation.annotationType().isAnnotationPresent(RestController.class)) {
return true;
}
}
return false;
}).collect(Collectors.toSet());
}
public WebConfig() {
Set<String> w = new HashSet<>();
for (Class<?> clazz : scanRestController()) {
//类上的注解
RequestMapping classRequestMapping = AnnotatedElementUtils.findMergedAnnotation(clazz, RequestMapping.class);
IsWhiteList clazzIsWhiteList = clazz.getAnnotation(IsWhiteList.class);
if (classRequestMapping != null && clazzIsWhiteList != null) {
//直接放行当前控制器的所有方法
w.addAll(Arrays.stream(classRequestMapping.value()).map(path -> {
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/")) {
path = path + "/";
}
return path + "**";
}).toList());
continue;
}
//看方法上是否有白名单注解
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
IsWhiteList methodIsWhiteList = method.getAnnotation(IsWhiteList.class);
RequestMapping methodRequestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
if (methodRequestMapping != null && methodIsWhiteList != null) {
List<List<String>> list1 = Arrays.stream(methodRequestMapping.value()).map(path -> {
if (!path.startsWith("/")) {
path = "/" + path;
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
List<String> list = new ArrayList<>();
if (classRequestMapping != null && CollUtil.isNotEmpty(Arrays.asList(classRequestMapping.value()))) {
for (String p : classRequestMapping.value()) {
if (!p.startsWith("/")) {
p = "/" + p;
}
if (p.endsWith("/")) {
p = p.substring(0, p.length() - 1);
}
list.add(p + path);
}
} else {
list.add(path);
}
return list;
}).toList();
w.addAll(list1.stream().flatMap(List::stream).toList());
}
}
}
log.info("加载路由白名单:{}", w);
whiteList.addAll(w);
//网站图标
whiteList.add("/favicon.ico");
//druid console
whiteList.add("/druid/**");
//knife4j
whiteList.add("/doc.html/**");
whiteList.add("/static/**");
whiteList.add("/swagger-resources");
whiteList.add("/**webjars/**");
whiteList.add("/v3/**");
}
@Override
public void addInterceptors(@NotNull InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.addPathPatterns("/**")
.excludePathPatterns(whiteList);
// 注册jsonBody拦截器 用于标识是否需要JsonResult返回
registry.addInterceptor(new JsonBodyInterceptor());
// 注册用户类型拦截器 用于校验当前用户是否匹配操作客户端
registry.addInterceptor(new UserTypeInterceptor());
// 注册开放接口认证拦截器 用于校验第三方是否授权
registry.addInterceptor(new OpenApiAuthInterceptor())
.addPathPatterns("/open/**");
}
@Bean
public FilterRegistrationBean<BodyWrapperFilter> loggingFilter() {
FilterRegistrationBean<BodyWrapperFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new BodyWrapperFilter());
registrationBean.addUrlPatterns("/open/*"); // 指定过滤的URL模式
registrationBean.setOrder(1000);
return registrationBean;
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "OPTION", "PUT", "DELETE")
.allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
"Access-Control-Request-Headers", "Authorization", "Token", "*")
.allowCredentials(true)
.maxAge(3600);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}