引入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/*");
}
}