解决saToken在Filter过滤器中使用时,报SaTokenContext 上下文尚未初始化错误
@Component
@Slf4j
public class AuthorizeFilter implements GlobalFilter {@Override@NonNullpublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest newRequest = exchange.getRequest().mutate().header("X-User-ID", StpUtil.getLoginId(-1L).toString()).build();ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();return chain.filter(newExchange);}
}
以上代码运行时会报SaTokenContext 上下文尚未初始化错误
1.解决方法
既然没有SaTokenContext,那么就设置SaTokenContext就好了,所以就添加一个Filter设置SaTokenContext。
@Component
@Slf4j
@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER)
public class SaTokenContextFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//设置sa-token上下文log.info("SaTokenContextFilter set sa-token context");SaReactorSyncHolder.setContext(exchange);return chain.filter(exchange).doFinally(t -> {SaReactorSyncHolder.clearContext();});}
}
2.问题原因
经过跟踪发现是 SaTokenContextForThreadLocalStaff 类中以下方法box为空抛出的错误
public static SaTokenContextModelBox getModelBox() {SaTokenContextModelBox box = (SaTokenContextModelBox)modelBoxThreadLocal.get();if (box == null) {throw (new SaTokenContextException("SaTokenContext 上下文尚未初始化")).setCode(10002);} else {return box;}}
3.解决问题过程
参考官网:自定义 SaTokenContext 指南 的解决方法
/*** SaTokenContext 上下文初始化过滤器 (基于 Servlet)*/
@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER)
public class SaTokenContextFilterForServlet implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {try {SaTokenContextServletUtil.setContext((HttpServletRequest) request, (HttpServletResponse) response);chain.doFilter(request, response);} finally {SaTokenContextServletUtil.clearContext();}}
}
发现官网方法适用于Servlet,并不适用于webflux,
于是参考: sa-token-starter/sa-token-reactor-spring-boot3-starter · dromara/Sa-Token - 码云 - 开源中国
@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER)
public class SaTokenContextFilterForReactor implements WebFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {return chain.filter(exchange).contextWrite(ctx -> SaReactorHolder.setContext(ctx, exchange, chain)).doFinally(r -> {// 在流式上下文中保存的数据会随着流式操作的结束而销毁,所以此处无需手动清除数据});}}
改为1.的最终结果
4.注意事项
- 注意Filter的@Order顺序,SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER = -104
- 清除SaTokenContext必须在 chain.filter(exchange).doFinally(/* clear */) 中操作,否则会异步提前把SaTokenContext清楚了
- 可以通过 SaManager.getSaTokenContext().isValid() 判断 SaTokenContext 是否初始化
