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 whiteList = new ArrayList<>(); /** * 扫描所有Controller */ private static Set> scanRestController() { //需要过滤出JsonBody Set> 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 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> list1 = Arrays.stream(methodRequestMapping.value()).map(path -> { if (!path.startsWith("/")) { path = "/" + path; } if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } List 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 loggingFilter() { FilterRegistrationBean 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/"); } }