SpringMVC拦截器+ThreadLocal统一token处理标题

1、JWT工具类

  • JWT的原理及使用
  • 封装JWT的操作为一个工具类,降低代码冗余量,方便使用
  • JWT工具类代码
package com.tanhua.commons.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;

import java.util.Date;
import java.util.Map;

public class JwtUtils {

    // TOKEN的有效期1小时(S)
    private static final int TOKEN_TIME_OUT = 3_600;

    // 加密使用的密钥,解析token数据时要填写此密钥
    private static final String TOKEN_SECRET = "itcast";

    /**
     * 生成token
     * @param params 有效载荷,token携带的供当前请求使用的信息
     * @return 生成的token
     */
    public static String getToken(Map params) {
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, TOKEN_SECRET) // 加密方式
                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) // 过期时间戳
                .addClaims(params) // token携带的信息
                .compact();
    }

    /**
     * 获取Token中的claims携带的信息
     * @param token 要解析的token
     * @return 解析后的token携带的信息
     */
    public static Claims getClaims(String token) {
        return Jwts.parser()
                .setSigningKey(TOKEN_SECRET)
                .parseClaimsJws(token).getBody();
    }

    /**
     * 校验token是否有效,是否有效 true-有效,false-失效
     * @param token 需要校验的token
     * @return 该token是否有效
     */
    public static boolean verifyToken(String token) {
        if (StringUtils.isEmpty(token)) { // token为空,无效
            return false;
        }

        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(TOKEN_SECRET) // 创建token时使用的密钥
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) { // 解析token时抛出异常,token无效
            return false;
        }

        return true;
    }
}

2、使用ThreadLocal封装获取请求用户信息的工具类

  • 学习ThreadLocal类
  • ThreadLocal类是线程独立的
  • ThreadLocal用于存储从解析本次请求的token得到的用户信息,方便后续层次的使用,降低代码冗余量
  • 工具类代码
package com.tanhua.server.interceptor;

import com.tanhua.model.domain.User;

/**
 * 工具类:实现向ThreadLocal存储数据的方法
 */
public class UserHolder {

    private static ThreadLocal<User> t1 = new ThreadLocal<>();

    // 将当前用户对象,存入ThreadLocal
    public static void set(User user) {
        t1.set(user);
    }

    // 从当前线程,获取用户对象
    public static User get() {
        return t1.get();
    }

    // 从当前线程,获取用户对象的id
    public static Long getUserId() {
        return t1.get().getId();
    }

    // 从当前线程,获取用户对象的手机号码
    public static String getMobile() {
        return t1.get().getMobile();
    }

    // 清空当前线程ThreadLocal
    public static void remove() {
        t1.remove();
    }
}

3、SpringMVC拦截器

package com.tanhua.server.interceptor;

import com.tanhua.commons.utils.JwtUtils;
import com.tanhua.model.domain.User;
import io.jsonwebtoken.Claims;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TokenInterceptor implements HandlerInterceptor { // 创建自定义拦截器要实现HandlerInterceptor接口

    /**
     * 处理请求之前执行的拦截方法
     * @param request 此次请求的request参数
     * @param response 此次请求的response参数
     * @param handler
     * @return 返回true放行,返回false拦截本次请求
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1、获取请求头
        String token = request.getHeader("Authorization");
        // 2、使用工具类,判断token是否有效
        boolean verifyToken = JwtUtils.verifyToken(token);
        // 3、token无效,返回状态码401,拦截后续请求
        if (!verifyToken) {
            response.setStatus(401);
            return false;
        }
        // 4、如果token正常可用,解析token,获取id和手机号码,放行
        Claims claims = JwtUtils.getClaims(token);
        String mobile = (String) claims.get("mobile");
        Integer id = (Integer) claims.get("id");

        // 构造User对象,存入ThreadLocal
        User user = new User();
        user.setId(Long.valueOf(id));
        user.setMobile(mobile);

		// 将解析token得到的用户信息保存到ThreadLocal对象中
        UserHolder.set(user);

        return true;
    }

    /**
     * 本次请求完成之后执行的拦截器方法,清空当前ThreadLocal内的内容,预防内存泄漏
     * @param request 此次请求的request参数
     * @param response 此次请求的response参数
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.remove();
    }
}

  • 创建拦截器类后,还要创建专门的配置类对拦截器进行配置,配置类与拦截器类处于同一包下
package com.tanhua.server.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration // 配置类必须添加@Configuration注解
public class WebConfig implements WebMvcConfigurer { // 自定义的SpringMVC的配置类要实现WebMvcConfigurer接口
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenInterceptor())
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/user/login", "/user/loginVerification"); // 排除对登录请求的拦截
    }
}

4、总结

  1. 前端发起登录请求,登录成功后,后端返回一个封装了登录用户信息的token给前端,后续前端的请求必须携带这个token。
  2. 前端发起除登录以外的请求时,必须携带之前登录成功后后端返回的token,后端的拦截器会拦截这些请求并校验token。
  3. 校验token成功,放行并且保存了此次请求用户信息(从解析的token中获得),用于后续层次对请求的处理。
  4. 校验失败,拦截本次请求。
  5. 请求示意图如下
    请求示意图
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值