跳转至

引入JWT,配置拦截器

生成JWT

创建一个构造类,用于存储当前登录用户的各种信息

package org.example.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Builder-自动生成建造者模式的 API,用于实例化对象。
 * @AllArgsConstructor-自动生成全参数构造函数(包含所有字段的构造函数)。
 * @NoArgsConstructor-自动生成无参构造函数。
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser {

    private String accountNo;

    private String username;

    private String headImg;

    private String mail;

    private String phone;

    private String auth;

}

一般我们是登录以后,根据用户的各种信息生成token

LoginUser loginUser = LoginUser.builder().build();
BeanUtils.copyProperties(accountDO,loginUser);
// 生成token
String token = JWTUtil.genreJsonWebToken(loginUser);

拦截器

通过实现HandlerInterceptor提供的方法对接口按特定的条件进行拦截,主要逻辑是在preHandle方法

package org.example.interceptor;

import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.example.enums.BizCodeEnum;
import org.example.model.LoginUser;
import org.example.utils.CommonUtil;
import org.example.utils.JWTUtil;
import org.example.utils.JsonData;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/**
 * HandlerInterceptor:Spring MVC 提供的拦截器接口,用于在请求处理的不同阶段(预处理、后处理、完成后)添加自定义逻辑。
 * LoginInterceptor:自定义登录验证拦截器,自定义实现HandlerInterceptor中的preHandle、postHandle、afterCompletion三个方法
 * Spring MVC 在处理请求时,会触发 LoginInterceptor 的 preHandle 方法进行验证。
 * 验证通过后,用户信息存入 ThreadLocal,供后续组件使用。
 * 请求处理完成后,afterCompletion 清理 ThreadLocal,释放资源。
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    public ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (HttpMethod.OPTIONS.toString().equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpStatus.SC_NO_CONTENT);
            return true;
        }
        String token = request.getHeader("token");
        if (StringUtils.isBlank(token)) {
            token = request.getParameter("token");
        }
        if (StringUtils.isNotBlank(token)) {
            Claims claims = JWTUtil.checkJWT(token);
            if (claims==null) {
                CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));
                return false;
            }

            String accountNo = claims.get("account_no").toString();
            String headImg = claims.get("head_img").toString();
            String username = claims.get("username").toString();
            String mail = claims.get("mail").toString();
            String phone = claims.get("phone").toString();
            String auth = claims.get("auth").toString();

            LoginUser loginUser = LoginUser.builder().
                    accountNo(accountNo)
                    .username(username)
                    .headImg(headImg)
                    .mail(mail)
                    .phone(phone).auth(auth).build();
            // 透传用户信息,两种方式
            // request.setAttribute("user",loginUser);
            /**
             * 将用户信息存储到当前线程的ThreadLocal中
             * 后续的控制器 / 服务可通过threadLocal.get()获取当前用户
             */
            threadLocal.set(loginUser);
            return true;
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        threadLocal.remove();
    }
}

排除掉一些不需要进行token校验等流程的接口,比如登录等接口

package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.example.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Slf4j
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    /**
     * 这里是初步拦截,最终会走到InterceptorConfig那边,如果返回true的话会放行,否则会将接口拦截掉(根据token进行判断拦截)
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                // 添加拦截的路径
                .addPathPatterns("/account/**","/traffic/**")
                // 排除不拦截的
                .excludePathPatterns("/account/register","/account/login","/notify/*");
    }
}