1.实现思路
1).全局统一拦截,除带访问带有登录的请求路径以为所有请求全部拦截进行Token检验
2).在用户登录之后生成Token并将token返回给前台
3).实现细节如下
2.版本依赖SpringBoot+jwt
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!--jwt token验权-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
3.全局统一拦截的实现
WebMvcConfigurer 接口详解https://www.cnblogs.com/pjjlt/p/11005811.html
/**
* 功能描述:设置拦截器.
* 打上configuration 注解,标注为配置项
*
* @author:hdh
* @date: 11:01
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 注入 token 拦截器
@Autowired
private TokenInterceptor interceptor;
/**
* 重写添加拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加自定义拦截器,并拦截对应 url
registry.addInterceptor(interceptor).excludePathPatterns("/login/**");
}
}
4.拦截器实现
HandlerInterceptorAdapter 接口:重写拦截器 return true;放行
/**
* 功能描述:创建一个 token 拦截器.
* 需要继承 HandlerInterceptorAdapter,并且声明为spring的组件
*
* @author:hdh
* @date: 2020/12/14 11:03
*/
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
// 注入jwt工具类
@Autowired
private JwtUtils jwtUtils;
@Autowired
private SessionUtils sessionUtils;
// 重写 前置拦截方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1、从请求头中获取token
String token = request.getHeader("token");
// 2、判断 token 是否存在
if (token == null || "".equals(token)) {
throw new TokenRuntimeException("token 获取失败");
}
//3、token是否过期
boolean tokenExpired = jwtUtils.isTokenExpired(token);
if (tokenExpired) {
throw new TokenRuntimeException("token 过期");
}
// 4、解析token
Map<String, Claim> checkMap = jwtUtils.checkToken(token);
if (checkMap.size() == 0) {
throw new TokenRuntimeException("token 解析错误");
}
//5、将userId存入session有效期2小时
sessionUtils.setUserId(request, jwtUtils.getUserId(token));
String userId = sessionUtils.getUserId();
return true;
}
}
5.全局异常处理和全局返回结果处理
/**
*功能描述:自定义 token 异常
*@author:hdh
*@date: 2020/12/14 11:06
*/
@Data
public class TokenRuntimeException extends RuntimeException{
private Integer code = 401;
private String msg;
public TokenRuntimeException(String msg) {
this.msg = msg;
}
}
/**
*功能描述:全局异常处理
*@author:hdh
*@date: 2020/12/14 11:05
*/
@RestControllerAdvice
public class SysRuntimeExceptionHandler {
@ExceptionHandler(TokenRuntimeException.class)
public ResponseEntity<String> tokenRuntimeException(TokenRuntimeException e) {
e.printStackTrace();
return ResultVo.createResponseEntity(ResultVo.CODE_UNAUTHORIZED, e.getMsg());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handlerException(Exception e) {
e.printStackTrace();
return ResultVo.createResponseEntity(ResultVo.SYSTEM_ERROR, e.getMessage());
}
}
package com.java110.vo;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.io.Serializable;
/**
*功能描述:ResultVo
*@author:hdh
*@date: 2020/12/14 11:07
*/
public class ResultVo implements Serializable {
public static final int CODE_ERROR = 404;// 未知异常
public static final int CODE_OK = 0; // 成功
public static final int SUCCESS = 0; // 成功
public static final int CODE_MACHINE_OK = 0; // 成功
public static final int CODE_MACHINE_ERROR = -1; // 未知异常
public static final int CODE_UNAUTHORIZED = 401; //认证失败
public static final int CODE_WECHAT_UNAUTHORIZED = 1401; //认证失败
public static final int SYSTEM_ERROR = 500; //系统错误
public static final int ORDER_ERROR = 500; //订单调度异常
public static final String MSG_ERROR = "未知异常";// 未知异常
public static final String MSG_OK = "成功"; // 成功
public static final String MSG_UNAUTHORIZED = "认证失败"; //认证失败
public static final int DEFAULT_RECORD = 1;
public static final int DEFAULT_TOTAL = 1;
// 分页页数
private int page;
// 行数
private int rows;
//页数
private int records;
// 总记录数
private int total;
//状态嘛
private int code;
//错误提示
private String msg;
//数据对象
private Object data;
public ResultVo() {
}
public ResultVo(int code, String msg) {
this.code = code;
this.msg = msg;
}
public ResultVo(Object data) {
this.code = CODE_OK;
this.msg = MSG_OK;
this.data = data;
}
public ResultVo(int records, int total, Object data) {
this.code = CODE_OK;
this.msg = MSG_OK;
this.records = records;
this.total = total;
this.data = data;
}
public ResultVo(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public ResultVo(int records, int total, int code, String msg, Object data) {
this.records = records;
this.total = total;
this.code = code;
this.msg = msg;
this.data = data;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public int getRecords() {
return records;
}
public void setRecords(int records) {
this.records = records;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return JSONObject.toJSONString(this, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteDateUseDateFormat);
}
/**
* 创建ResponseEntity对象
*
* @param data 数据对象
* @return
*/
public static ResponseEntity<String> createResponseEntity(Object data) {
ResultVo resultVo = new ResultVo(DEFAULT_RECORD, DEFAULT_TOTAL, data);
ResponseEntity<String> responseEntity = new ResponseEntity<String>(resultVo.toString(), HttpStatus.OK);
return responseEntity;
}
/**
* 成功通用回复
*
* @return
*/
public static ResponseEntity<String> success() {
ResultVo resultVo = new ResultVo(CODE_OK, MSG_OK);
ResponseEntity<String> responseEntity = new ResponseEntity<String>(resultVo.toString(), HttpStatus.OK);
return responseEntity;
}
/**
* 创建ResponseEntity对象
*
* @param records 页数
* @param total 总记录数
* @param data 数据对象
* @return
*/
public static ResponseEntity<String> createResponseEntity(int records, int total, Object data) {
ResultVo resultVo = new ResultVo(records, total, data);
ResponseEntity<String> responseEntity = new ResponseEntity<String>(resultVo.toString(), HttpStatus.OK);
return responseEntity;
}
/**
* 页面跳转
*
* @param url
* @return
*/
public static ResponseEntity<String> redirectPage(String url) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.LOCATION, url);
ResponseEntity<String> responseEntity = new ResponseEntity<String>("", headers, HttpStatus.FOUND);
return responseEntity;
}
/**
* 创建ResponseEntity对象
*
* @param code 状态嘛
* @param msg 返回信息
* @param data 数据对象
* @return
*/
public static ResponseEntity<String> createResponseEntity(int code, String msg, Object data) {
ResultVo resultVo = new ResultVo(code, msg, data);
ResponseEntity<String> responseEntity = new ResponseEntity<String>(resultVo.toString(), HttpStatus.OK);
return responseEntity;
}
/**
* 创建ResponseEntity对象
*
* @param code 状态嘛
* @param msg 返回信息
* @return
*/
public static ResponseEntity<String> createResponseEntity(int code, String msg) {
ResultVo resultVo = new ResultVo(code, msg);
ResponseEntity<String> responseEntity = new ResponseEntity<String>(resultVo.toString(), HttpStatus.OK);
return responseEntity;
}
/**
* 创建ResponseEntity对象
*
* @param records 页数
* @param total 总记录数
* @param code 状态嘛
* @param msg 返回信息
* @param data 数据对象
* @return
*/
public static ResponseEntity<String> createResponseEntity(int records, int total, int code, String msg, Object data) {
ResultVo resultVo = new ResultVo(records, total, code, msg, data);
ResponseEntity<String> responseEntity = new ResponseEntity<String>(resultVo.toString(), HttpStatus.OK);
return responseEntity;
}
}
6.Jwt工具类
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.car.oa.exception.TokenRuntimeException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtUtils {
//设置过期时间
private static final long EXPIRE_DATE = 60*1000*120;
//token秘钥
private static final String TOKEN_SECRET = "密钥";
/**
* 功能描述:生成token
*
* @author:hdh
* @date: 2020/12/11 13:51
*/
public String setToken(String userId) {
String token = "";
try {
//过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_DATE);
//秘钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头部信息
Map<String, Object> header = new HashMap<>();
header.put("typ", "JWT");
header.put("alg", "HS256");
//携带username,password信息,生成签名
token = JWT.create()
.withHeader(header)
.withClaim("userId", userId)
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRE_DATE))
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return token;
}
/**
* 解密Token
*
* @param token
* @return
* @throws Exception
*/
public Map<String, Claim> checkToken(String token) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build();
jwt = verifier.verify(token);
} catch (Exception e) {
throw new TokenRuntimeException("token 解析异常");
// token 校验失败, 抛出Token验证非法异常
}
return jwt.getClaims();
}
/**
* 根据Token获取user_id
*
* @param token
* @return user_id
*/
public String getUserId(String token) {
Map<String, Claim> claims = checkToken(token);
Claim user_id_claim = claims.get("userId");
if (null == user_id_claim || StringUtils.isEmpty(user_id_claim.asString())) {
// token 校验失败, 抛出Token验证非法异常
}
return user_id_claim.asString().substring(0, 11);
}
/**
* 判断 token 是否过期
*/
public boolean isTokenExpired(String token) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build();
jwt = verifier.verify(token);
} catch (Exception e) {
throw new TokenRuntimeException("token 过期");
// token 校验失败, 抛出Token验证非法异常
}
Date expiresAt = jwt.getExpiresAt();
//判断当前时间是否在过期时间之前 token时间小于当前时间返回true token时间大于当前时间返回false
return expiresAt.before(new Date());
}
}
7.sessionUtils
如何获取到token中的userId
@Component
/**
*功能描述:session工具类
*@author:hdh
*@date: 2020/12/14 10:10
*/
public class SessionUtils {
@Autowired
HttpServletRequest request;
//从session中获取userId
public String getUserId() {
HttpSession session = request.getSession();
String userId = String.valueOf(session.getAttribute("userId")).substring(0, 11);
return userId;
}
public void setUserId(HttpServletRequest request, String userId) {
HttpSession session = request.getSession();
session.setMaxInactiveInterval(60 * 120);
session.setAttribute("userId", userId);
}
}