视频网址链接哪里找昆明排名优化
背景:在手写登录的过程中需要继承sa-token框架,并使用sa-token的过滤器功能,那全局过滤器怎么写呢 ?我们一起来看看吧。
根据sa-token的官方文档:
https://sa-token.cc/doc.html#/up/global-filter
如下图所示:下面是最简单的一种sa-token过滤器的实现方式,这种写法简单,但是会有一些问题。
第一种写法:
相较于第一种写法,配置更为全面的过滤器写法:
登录中的具体写法:第一种,不设置Order的写法:
@Configuration
@Slf4j
public class SaTokenConfigure implements WebMvcConfigurer {/*** 白名单**/private static String[] EXCLUDE_PATH = {"/login/**","/doc.html","/service-worker.js","/swagger-resources","/webjars/**","/v3/api-docs/**","/favicon.ico","/ocr/**","/location/**"};/*** 注册拦截器***/@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())).addPathPatterns("/**")//设置白名单.excludePathPatterns(EXCLUDE_PATH);}/*** 全局过滤器** @return cn.dev33.satoken.filter.SaServletFilter**/@Beanpublic SaServletFilter getSaServletFilter() {return new SaServletFilter()// 拦截与排除 path.addInclude("/**").addExclude(EXCLUDE_PATH)// 全局认证函数.setAuth(obj -> {// ...log.info("这里是全局认证函数");//这里可以写判断是否登录的逻辑,比如sa-token是有工具类StpUtil.isLogin()来判断当前请求的用户是否已经登录了,如果登录过滤器则放行,否则的话拦截,比如用户未登录的情况下,访问了一些在白名单之外的url地址,那此时就会经过这个位置,这里可以写校验逻辑,还有一些登录上下文的初始化逻辑。if(StpUtil.isLogin()){//Thread_local实现的上下文工具类//每次先清除一下上下文内容,TMSSsoContextHelper.clearSession();//重新初始化这个容器,其实也就是从sa-session中取得内容放到上下文信息中,以方便后续使用。initTMSSsoContextIfLogin();}else {//这里抛出了未登录的异常,会被下面的setError函数捕获并处理相关逻辑。throw new NotLoginException("未登录","web","");}})// 异常处理函数.setError(e -> {log.info("------------->NotLogin-------------");// 1. 设置响应头为 JSON 格式SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");// 2. 捕获未登录异常,这样设置上,会响应到前台具体的 如“请先登录”的提示。其实也可以在全局拦截器那捕获这个未登录的异常,提示未登录请重新登陆,也可以在这里进行包装后返回提示,如果这里不写和全局捕获那都不写的话,就会返回500 内部服务错误这种不好的信息到前台,所以这些需要进行响应信息的设置包装。if (e instanceof NotLoginException) {SaHolder.getResponse().setStatus(401); // 401 Unauthorizedreturn SaResult.error("请先登录").setCode(401);}// 3. 其他异常返回 500SaHolder.getResponse().setStatus(500);return SaResult.error("服务器繁忙,请稍后重试").setCode(500);})// 前置函数:在每次认证函数之前执行.setBeforeAuth(obj -> {//设置跨域响应头SaHolder.getResponse().setHeader("Access-Control-Allow-Origin", "*").setHeader("Access-Control-Allow-Methods", "*").setHeader("Access-Control-Max-Age", "3600").setHeader("Access-Control-Allow-Headers", "*");// 如果是预检请求,则立即返回到前端SaRouter.match(SaHttpMethod.OPTIONS).free(r -> System.out.println("--------OPTIONS预检请求,不做处理")).back();});}/*** 跨域请求设置** @param registry**/@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOriginPatterns("*").allowedMethods("*").allowCredentials(false).maxAge(3600);}/*** 设置上下文信息* 其实就是通过登录之后,登录的具体逻辑里面会初始化sa-session对象,然后登录后调用其他请求的时候,这个sasession容器中肯定是有相关信息了,直接初始化放到Thread_local对象中即可让别的程序中现用现取得了。* @Date **/private void initTMSSsoContextIfLogin() {SaSession saSession = StpUtil.getSession();String tokenValue = StpUtil.getTokenValue();Long userId = (Long) saSession.get(BaseConstant.CURRENT_USER_ID);String tenantIds = (String)saSession.get(BaseConstant.CURRENT_TENANT_ID);String phone = (String)saSession.get(BaseConstant.CURRENT_USER_PHONE);//供service使用 当前登录用户关联到审核的企业后:将租户放入RPC容器if (ObjectUtil.isNotEmpty(tenantIds)) {RpcContext.getContext().setAttachment(BaseConstant.RPC_CONTEXT_TENANT_ID, tenantIds);}TMSSsoContextInfo tmsSsoContextInfo = TMSSsoContextInfo.builder().token(tokenValue).userId(userId+"").tenantId(tenantIds).phone(phone).build();TMSSsoContextHelper.setTMSSsoContextInfo(tmsSsoContextInfo);}
}
第二种,设置Order的写法:
这种是先创建了FilterRegistrationBean对象,同时给这个对象set值的时候,new 了一个SaServletFilter对象,并同时设置上了相关属性逻辑。
@Bean
public FilterRegistrationBean<SaServletFilter> saFilterRegistration() {FilterRegistrationBean<SaServletFilter> frBean = new FilterRegistrationBean<>();frBean.setFilter(new SaServletFilter().addInclude("/**").addExclude("/favicon.ico").setAuth(obj -> {SaRouter.match("/**", "/user/doLogin", () -> StpUtil.checkLogin());}).setError(e -> SaResult.error(e.getMessage())));frBean.setOrder(-101); // 确保在 Spring Security 过滤器之前执行frBean.addUrlPatterns("/**"); // 显式指定 URL 模式(避免覆盖问题)return frBean;
}
或者
可以先创建一个SaServletFilter对象,并设置上相关的属性逻辑后,然后再去创建FilterRegistrationBean对象,然后再给这个对象设置Filter属性及Order等属性。具体如下:
@Bean
public FilterRegistrationBean<SaServletFilter> saFilterRegistration() {// 1. 创建并配置 SaServletFilterSaServletFilter saServletFilter = new SaServletFilter().addInclude("/**").addExclude(EXCLUDE_PATH).setAuth(obj -> { /* 认证逻辑 */ }).setError(e -> { /* 异常处理 */ }).setBeforeAuth(r -> { /* 跨域配置 */ });// 2. 包装为 FilterRegistrationBeanFilterRegistrationBean<SaServletFilter> frBean = new FilterRegistrationBean<>();frBean.setFilter(saServletFilter);frBean.setOrder(-99); frBean.addUrlPatterns("/**"); // 使用 /** 匹配所有路径return frBean;
}
将通过第一种写法的全局拦截器代码进行优化(设置上相关执行顺序了)后如下:
改写思路:
首先,我需要回忆Spring
Boot中如何注册过滤器。通常,通过FilterRegistrationBean
可以注册Servlet过滤器,并设置其顺序、URL模式等属性。而直接返回SaServletFilter
作为Bean,Spring
Boot会自动将其注册为过滤器,但可能无法灵活设置优先级。接下来,我需要将现有的
getSaServletFilter()
方法转换为创建FilterRegistrationBean
实例。原方法中,用户配置了拦截路径、排除路径、认证函数、异常处理函数以及前置函数。这些配置需要保留,但需要包装到FilterRegistrationBean
中。步骤可能如下:
创建一个
FilterRegistrationBean
实例。将原有的
SaServletFilter
配置移动到该实例中。设置过滤器的顺序为-99。
确保URL模式的正确设置,可能需要显式指定
addUrlPatterns
,尽管原代码中已经通过addInclude
设置了路径,但为了保险起见,可能在FilterRegistrationBean
中再次指定。需要注意的地方包括:
确保
SaServletFilter
的所有配置(如addInclude, addExclude, setAuth, setError, setBeforeAuth)都被正确转移到新的FilterRegistrationBean
中。检查是否有任何依赖于Spring Boot自动配置的部分,可能需要显式设置。
确认优先级设置是否正确,-99的值是否合适,这取决于系统中其他过滤器的优先级设置,比如Spring Security通常使用-100,所以-99会让Sa-Token过滤器在其之后执行,但用户可能需要根据实际需求调整。
此外,用户提到要设置优先级为-99,我需要确保在
FilterRegistrationBean
中通过setOrder
方法设置。例如:frBean.setOrder(-99);
在代码转换过程中,还需要测试以确保修改后的过滤器仍然按预期工作,包括拦截正确的路径、处理异常、设置响应头等。特别是跨域设置和认证逻辑是否在正确的位置执行。最后,需要检查是否有其他配置可能影响过滤器的行为,比如
WebMvcConfigurer
中的其他设置,确保没有冲突。
以下是修改后的代码,将 SaServletFilter 的注册方式改为通过 FilterRegistrationBean,并设置优先级为 -99:
@Configuration
@Slf4j
public class SaTokenConfigure implements WebMvcConfigurer {// ... 其他代码保持不变 .../*** 注册全局过滤器,并设置优先级为 -99*/@Beanpublic FilterRegistrationBean<SaServletFilter> saFilterRegistration() {// 1. 创建 SaServletFilter 实例并配置SaServletFilter saServletFilter = new SaServletFilter().addInclude("/**").addExclude(EXCLUDE_PATH).setAuth(obj -> {log.info("这里是全局认证函数");if(SsoManager.isLogin()){TMSSsoContextHelper.clearSession();initTMSSsoContextIfLogin();} else {throw new NotLoginException("未登录", "web", "");}}).setError(e -> {log.info("------------->NotLogin-------------");SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");if (e instanceof NotLoginException) {SaHolder.getResponse().setStatus(401);return SaResult.error("请先登录").setCode(401);}SaHolder.getResponse().setStatus(500);return SaResult.error("服务器繁忙,请稍后重试").setCode(500);}).setBeforeAuth(obj -> {SaHolder.getResponse().setHeader("Access-Control-Allow-Origin", "*").setHeader("Access-Control-Allow-Methods", "*").setHeader("Access-Control-Max-Age", "3600").setHeader("Access-Control-Allow-Headers", "*");SaRouter.match(SaHttpMethod.OPTIONS).free(r -> System.out.println("--------OPTIONS预检请求,不做处理")).back();});// 2. 包装成 FilterRegistrationBean 并设置优先级FilterRegistrationBean<SaServletFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(saServletFilter);registrationBean.setOrder(-99); // 设置优先级为-99registrationBean.addUrlPatterns("/*"); // 显式指定拦截路径(覆盖 addInclude 的配置)return registrationBean;}// ... 其他方法保持不变 ...
}
关键改动说明:
-
返回类型修改:
// 原返回类型 @Bean public SaServletFilter getSaServletFilter() { ... }// 新返回类型 @Bean public FilterRegistrationBean<SaServletFilter> saFilterRegistration() { ... }
-
显式设置优先级:
registrationBean.setOrder(-99); // 确保优先级高于 Spring Security(默认-100)
-
URL 模式显式声明:
registrationBean.addUrlPatterns("/*"); // 明确拦截所有路径
- 虽然
SaServletFilter
内部已通过.addInclude("/**")
声明,但FilterRegistrationBean
会覆盖该配置,建议保持一致
- 虽然
-
保持原有配置:
- 认证逻辑(
.setAuth
) - 异常处理(
.setError
) - 跨域配置(
.setBeforeAuth
) - 排除路径(
.addExclude(EXCLUDE_PATH)
)
- 认证逻辑(
注意事项:
-
优先级与 Spring Security 的关系:
- Spring Security 的过滤器链默认优先级为
-100
- 设置
-99
表示您的过滤器会在 Spring Security 之后执行 - 如果需要先于 Spring Security 执行鉴权,需设置为比
-100
更小的值(如-101
)
- Spring Security 的过滤器链默认优先级为
-
URL 匹配模式:
SaServletFilter.addInclude("/**")
匹配所有路径(包括子路径)FilterRegistrationBean.addUrlPatterns("/*")
匹配所有路径(但不会匹配子路径)- **建议统一使用
/***
以保证拦截范围一致
-
排除路径处理:
addExclude(EXCLUDE_PATH)
已通过SaServletFilter
配置- 需确保
EXCLUDE_PATH
中的路径不会被 Spring Security 或其他过滤器拦截
-
跨域配置冲突:
- 如果项目中同时存在其他跨域配置(如
@CrossOrigin
或CorsFilter
),需确保配置不冲突 - 建议统一在 一处 配置跨域逻辑(推荐在
setBeforeAuth
中配置)
- 如果项目中同时存在其他跨域配置(如
完整配置建议:
@Bean
public FilterRegistrationBean<SaServletFilter> saFilterRegistration() {// 1. 创建并配置 SaServletFilterSaServletFilter saServletFilter = new SaServletFilter().addInclude("/**").addExclude(EXCLUDE_PATH).setAuth(obj -> { /* 认证逻辑 */ }).setError(e -> { /* 异常处理 */ }).setBeforeAuth(r -> { /* 跨域配置 */ });// 2. 包装为 FilterRegistrationBeanFilterRegistrationBean<SaServletFilter> frBean = new FilterRegistrationBean<>();frBean.setFilter(saServletFilter);frBean.setOrder(-99); frBean.addUrlPatterns("/**"); // 使用 /** 匹配所有路径return frBean;
}
梳理完毕,两种实现方式都练习🆗啦,大家选一个使用就行。
但是具体两个写法都i有什么优略呢?我们可以看下篇文章: 两种写法的区别 敬请期待~