package com.tcm.training.admin.config; import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.stp.StpUtil; import com.tcm.training.admin.common.xss.XssParamInterceptor; import com.tcm.training.admin.upload.LocalUploadProperties; import org.springframework.context.annotation.Configuration; 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.io.File; @Configuration public class SaTokenWebConfig implements WebMvcConfigurer { private final XssParamInterceptor xssParamInterceptor; private final LocalUploadProperties localUploadProperties; public SaTokenWebConfig(XssParamInterceptor xssParamInterceptor, LocalUploadProperties localUploadProperties) { this.xssParamInterceptor = xssParamInterceptor; this.localUploadProperties = localUploadProperties; } /** * 注册 Sa-Token 与 XSS 拦截器。 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SaInterceptor(handle -> handleAuth())) .addPathPatterns("/api/**", "/api/v1/**") .excludePathPatterns( localUploadProperties.getPathPrefix() + "/**" ); registry.addInterceptor(xssParamInterceptor) .addPathPatterns("/api/**", "/api/v1/**"); } /** * 按请求路径切换管理员鉴权与 APP 鉴权。 */ private void handleAuth() { if ("OPTIONS".equalsIgnoreCase(SaHolder.getRequest().getMethod())) { return; } String requestPath = SaHolder.getRequest().getRequestPath(); if (isAppApi(requestPath)) { // APP 端允许游客访问,只有真正需要用户身份的接口才在业务层显式要求登录。 return; } if (isPublicAdminApi(requestPath)) { return; } StpUtil.checkLogin(); } /** * 判断是否属于 APP 路由空间。 */ private boolean isAppApi(String requestPath) { return requestPath.startsWith("/api/app/") || requestPath.startsWith("/api/v1/app/"); } /** * 判断当前后台路由是否允许匿名访问。 */ private boolean isPublicAdminApi(String requestPath) { return "/api/auth/login".equals(requestPath) || "/api/auth/captcha".equals(requestPath) || requestPath.startsWith("/api/open/") || requestPath.startsWith("/api/v1/open/"); } /** * 将本地上传目录映射为静态资源,可通过 /uploads/** 访问。 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 转为绝对路径,确保 file: 协议正确。 String absPath = new File(localUploadProperties.getDir()).getAbsolutePath(); registry.addResourceHandler(localUploadProperties.getPathPrefix() + "/**") .addResourceLocations("file:" + absPath + "/"); } }