前言:在登陆的时候,响应给前端的时候,你需要将userId放入JWT中
public static String getToken(Long id){ Map<String, Object> claimMaps = new HashMap<>(); claimMaps.put("id",id); long currentTime = System.currentTimeMillis(); return Jwts.builder() .setId(UUID.randomUUID().toString()) .setIssuedAt(new Date(currentTime)) //签发时间 .setSubject("system") //说明 .setIssuer("heima") //签发者信息 .setAudience("app") //接收用户 .compressWith(CompressionCodecs.GZIP) //数据压缩方式 .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式 .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳 .addClaims(claimMaps) //cla信息 .compact(); }
参数id就是用户id,上面的代码流程就是创建JWT的流程,用户id放入了一个map集合里
最后生成jwt的字符串返回,接着
String token = AppJwtUtil.getToken(dbUser.getId().longValue()); Map<String,Object> map = new HashMap<>(); map.put("token",token); map.put("user",dbUser); return ResponseResult.okResult(map);
1
由上面可知,我们传了一个token给前端,前端再传请求的时候带上token,我们只需要解析token就能拿到用户id
1.1
创建一个AuthorizeFilter继承Ordered, GlobalFilter
public class AuthorizeFilter implements Ordered, GlobalFilter {
//再网关定义一个类,继承Ordered,GlobalFilter 重写方法 //GlobalFilter 是一个接口,它允许你定义全局的过滤器来修改进入网关的请求和离开网关的响应。 // Ordered 接口用于指定过滤器的执行顺序
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1.获取request和response对象 ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); //2.判断是否是登录 if(request.getURI().getPath().contains("/login")){ //放行 return chain.filter(exchange); } //3.获取token String token = request.getHeaders().getFirst("token"); //4.判断token是否存在 if(StringUtils.isBlank(token)){ response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } 1.2 你要解析token,并获取Claims //5.判断token是否有效 try { Claims claimsBody = AppJwtUtil.getClaimsBody(token); //是否是过期。。。。。 。。。。。。。 这里你取值的id要跟你传给前端的那个map的key一致 Object userId = claimsBody.get("id"); 1.3 既然拿到jwt的用户id,你就可以把id放入请求头当中,方便后续放入threallocal中 ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> { 这里 httpHeaders.add("userId", userId + ""); }).build(); exchange.mutate().request(serverHttpRequest); } catch (Exception e) { e.printStackTrace(); } //6.放行 return chain.filter(exchange); } /** * 优先级设置 值越小 优先级越高 * @return */ @Override public int getOrder() { return 0; } }
解析令牌private static final String SECRET_KEY = "your-secret-key"; // 你的密钥
public static Claims parseJwt(String jwt) {
try {
// 解析JWT字符串
return Jwts.parser()
.setSigningKey(SECRET_KEY) // 密钥,需要与签发JWT时使用的密钥相同
.parseClaimsJws(jwt) // 解析JWT字符串
.getBody(); // 获取claims body
} catch (Exception e) {
// 解析失败,可能是密钥不正确、JWT已过期等原因
e.printStackTrace();
return null;
}
}
2
当你的请求头里有了用户的id,接着你就可以在springmvc的拦截器中放入这用户id
2.1
HandlerInterceptor是Spring MVC框架中的一个核心接口,用于实现拦截器功能,允许开发者在请求处理的不同阶段执行自定义逻辑。它参与到Spring MVC的请求处理生命周期中,但并不是Spring IoC容器Bean生命周期的一部分。HandlerInterceptor接口定义了三个主要方法,分别对应于请求处理的不同阶段:
- preHandle方法:
- 调用时机:在请求到达目标处理器(Handler)之前被调用。
- 功能:用于执行一些前置处理逻辑,如身份验证、权限检查、日志记录等。
- 返回值:返回
true
表示请求继续传递到处理器;返回false
则请求处理终止。
- postHandle方法:
- 调用时机:在请求处理程序执行之后,但在视图渲染之前被调用。
- 功能:用于执行一些后置处理逻辑,如修改模型数据、记录执行时间等。
- 特点:此方法的执行不影响控制器方法的执行结果,即不论控制器方法是否成功执行,此方法都会被调用。
- afterCompletion方法:
- 调用时机:在整个请求完成之后被调用,即在视图渲染完成并且响应已发送给客户端后执行。
- 功能:通常用于清理资源、记录最终日志等。
- 信息获取:可以在此方法中获取到处理程序的执行结果和可能的异常信息。
创建一个WmTokenInterceptor,继承HandlerInterceptor
我们在方法执行前将这个user对象放入ThreadLocal当中
public class WmThreadLocalUtil { public final static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL=new ThreadLocal<>(); public static void setUser(WmUser wmUser){ WM_USER_THREAD_LOCAL.set(wmUser); } public static WmUser getUser(){ return WM_USER_THREAD_LOCAL.get(); } public static void clear(){ WM_USER_THREAD_LOCAL.remove(); } }c创建工具类,threadLocal
是一个ThreadLocal<
WmUser>
类型的静态字段,它在类加载时就被初始化,并且可以被类的所有方法访问。每个线程都有它自己的threadLocal
值的副本,所以你可以在不同的线程中设置和获取不同的值,而不会相互干扰
public class WmTokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = request.getHeader("userId");
if (userId!=null){
WmUser wmUser = new WmUser();
wmUser.setId(Integer.valueOf(userId));
我们在每一个线程里创建线程的她的threadLocal,然后保存
WmThreadLocalUtil.setUser(wmUser);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
WmThreadLocalUtil.clear();
}
}
3:
这样,一个名叫WmTokenInterceptor的类就被创建出来了,但是他还没有被springmvc使用
,所以,我们还需要将他加入到springmvc的拦截器(interceptor)当中
3.1在你需要使用这个用户id的地方,创建一个配置类
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**"); } } 总结
总之,步骤就是
1:在gateway拦截请求,解析请求取出id。如
AuthorizeFilter implements Ordered, GlobalFilter
将id放入请求头
2:创建拦截器
public class WmTokenInterceptor implements HandlerInterceptor
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
在
preHandle方法中,将id填入ThreadLocal中即可
3:最后在项目模块的配置类加上mvc的配置类来识别你刚刚创建的拦截器
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**"); } }