JWT 全名 JSON WEB Token 主要作用为用户身份验证, 广泛应用与前后端分离项目当中.

JWT 的优缺点 : https://www.jianshu.com/p/af8360b83a9f

一、pom.xml 引入jar文件

        <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.10.7</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.10.7</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.10.7</version><scope>runtime</scope></dependency>

二、添加jwt自定义字段 application.yml

# jwt 配置
custom:jwt:# header:凭证(校验的变量名)header: token# 有效期1天(单位:s)expire: 5184000# secret: 秘钥(普通字符串)secret: aHR0cHM6Ly9teS5vc2NoaW5hLm5ldC91LzM2ODE4Njg=# 签发者issuer: test-kou

三、添加加密解密方法


import io.jsonwebtoken.*;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** JWT 工具类* <p>* jwt含有三部分:头部(header)、载荷(payload)、签证(signature)* (1)头部一般有两部分信息:声明类型、声明加密的算法(通常使用HMAC SHA256)* (2)载荷该部分一般存放一些有效的信息。jwt的标准定义包含五个字段:* - iss:该JWT的签发者* - sub: 该JWT所面向的用户* - aud: 接收该JWT的一方* - exp(expires): 什么时候过期,这里是一个Unix时间戳* - iat(issued at): 在什么时候签发的* (3)签证(signature) JWT最后一个部分。该部分是使用了HS256加密后的数据;包含三个部分** @author kou*/
@ConfigurationProperties(prefix = "custom.jwt")
@Data
@Component
public class JwtUtil {private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);// 秘钥private String secret;// 有效时间private Long expire;// 用户凭证private String header;// 签发者private String issuer;/*** 生成token签名** @param subject* @return*/public String generateToken(String subject) {final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;Date now = new Date();// 过期时间Date expireDate = new Date(now.getTime() + expire * 1000);//Create the Signature SecretKeyfinal byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(Base64.getEncoder().encodeToString(getSecret().getBytes()));final Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());final Map<String, Object> headerMap = new HashMap<>();headerMap.put("alg", "HS256");headerMap.put("typ", "JWT");//add JWT Parametersfinal JwtBuilder builder = Jwts.builder().setHeaderParams(headerMap).setSubject(subject).setIssuedAt(now).setExpiration(expireDate).setIssuer(getIssuer()).signWith(signatureAlgorithm, signingKey);logger.info("JWT[" + builder.compact() + "]");return builder.compact();}/*** 解析token** @param token token* @return*/public Claims parseToken(String token) {Claims claims = null;try {final byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(Base64.getEncoder().encodeToString(getSecret().getBytes()));claims = Jwts.parser().setSigningKey(apiKeySecretBytes).parseClaimsJws(token).getBody();logger.info("Parse JWT token by: ID: {}, Subject: {}, Issuer: {}, Expiration: {}", claims.getId(), claims.getSubject(), claims.getIssuer(), claims.getExpiration());} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException| IllegalArgumentException e) {logger.info("Parse JWT errror " + e.getMessage());return null;}return claims;}/*** 判断token是否过期** @param expiration* @return*/public boolean isExpired(Date expiration) {return expiration.before(new Date());}}

四、添加拦截方法,拦截token处理,我这边使用的是filter拦截处理,根据自己需求自定


import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;/*** 如果请求中(请求头或者Cookie)中存在JWT,则:* 1、解析JWT并查找对应的用户信息,然后加入request attribute中* 2、更新Cookie时间、更新JWT失效时间放入Header* <p>* OncePerRequestFilter 一次请求只进入一次filter*/
@Component
@Slf4j
public class JWTFilter extends OncePerRequestFilter {public static final String SECURITY_USER = "SECURITY_USER";// 设置不需要校验的路径private static final String[] NOT_CHECK_URL = {"/login", "/registered"};@Autowiredprivate JwtUtil jwtUtil;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 判断是否需要对token处理if (!isNotCheck(request.getRequestURI())) {// 获取tokenString token = getToken(request);if (StringUtils.isBlank(token)) {log.info("请求无效,原因:{} 为空!", jwtUtil.getHeader());request.setAttribute("exceptionCode", SystemParameters.TOKEN_IS_NULL.getIndex());request.setAttribute("exceptionMessage", "请求无效,原因:" + jwtUtil.getHeader() + " 为空!");request.getRequestDispatcher("/exception/authentication").forward(request, response);// throw new AuthenticationException(SystemParameters.TOKEN_IS_NULL.getIndex(),  "请求无效,原因:" + jwtUtil.getHeader() + " 为空!");return;}Claims claims = jwtUtil.parseToken(token);// 判断签名信息if (null != claims && !claims.isEmpty() && !jwtUtil.isExpired(claims.getExpiration())) {// 获取签名用户信息String userId = claims.getSubject();//获取相应的用户信息,可以在过滤器中先行获取,也可以先保存用户ID,在需要时进行获取// User user = usersService.findById(Long.valueOf(userId));request.setAttribute(SECURITY_USER, userId);Cookie jwtCookie = new Cookie(jwtUtil.getHeader(), URLEncoder.encode(token, "UTF-8"));jwtCookie.setHttpOnly(true);jwtCookie.setPath("/");response.addCookie(jwtCookie);response.addHeader(jwtUtil.getHeader(), token);} else {log.info("{} 无效,请重新登录!", jwtUtil.getHeader());request.setAttribute("exceptionCode", SystemParameters.AUTHENTICATION_FAILED.getIndex());request.setAttribute("exceptionMessage", jwtUtil.getHeader() + " 无效,请重新登录!");request.getRequestDispatcher("/exception/authentication").forward(request, response);// throw new AuthenticationException(SystemParameters.AUTHENTICATION_FAILED.getIndex(), jwtUtil.getHeader() + " 无效,请重新登录!");return;}}filterChain.doFilter(request, response);}/*** 获取token** @param request* @return 返回token*/private String getToken(HttpServletRequest request) {//先从header中获取tokenString token = request.getHeader(jwtUtil.getHeader());//再从cookie中获取if (StringUtils.isBlank(token)) {try {Cookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie cookie : cookies) {if (jwtUtil.getHeader().equals(cookie.getName())) {token = URLDecoder.decode(cookie.getValue(), "UTF-8");}}}} catch (Exception e) {log.error("Can NOT get jwt from cookie -> Message: {}", e);}}return token;}/*** 根据url判断是否需要校验,false需要校验** @param url* @return 是否需要校验*/private boolean isNotCheck(String url) {// 处理路径以"/" 结尾的"/"url = url.endsWith("/") ? url.substring(0, url.lastIndexOf("/")) : url;for (String path : NOT_CHECK_URL) {// 判断是否以 "/**" 结尾if (path.endsWith("/**")) {return url.startsWith(path.substring(0, path.lastIndexOf("/") + 1))|| url.equals(path.substring(0, path.lastIndexOf("/")));}// 判断url == pathif (url.equals(path)) {return true;}}return false;}}

五、配置过滤器


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 过滤器配置** @author kou*/
@Configuration
public class FilterConfig {@Autowiredprivate JWTFilter jwtFilter;@Beanpublic FilterRegistrationBean registrationBean() {FilterRegistrationBean registration = new FilterRegistrationBean();// 设置过滤器registration.setFilter(jwtFilter);// 设置拦截路径registration.addUrlPatterns("/rest/*");// 设置过滤器名称registration.setName("JWTFilter");// 设置过滤器执行顺序registration.setOrder(1);return registration;}
}

六、添加异常处理类


/*** 认证异常类** @author kou*/
public class AuthenticationException extends RuntimeException {private static final long serialVersionUID = 1L;//自定义错误码private Integer code;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}/*** Creates a new AuthenticationException.*/public AuthenticationException() {super();}/*** Constructs a new AuthenticationException.** @param message the reason for the exception*/public AuthenticationException(String message) {super(message);}/*** Constructs a new AuthenticationException.** @param cause the underlying Throwable that caused this exception to be thrown.*/public AuthenticationException(Throwable cause) {super(cause);}/*** Constructs a new AuthenticationException.** @param message the reason for the exception* @param cause   the underlying Throwable that caused this exception to be thrown.*/public AuthenticationException(String message, Throwable cause) {super(message, cause);}/*** Constructs a new AuthenticationException.** @param message the reason for the exception*/public AuthenticationException(int code, String message) {super(message);this.code = code;}/*** Constructs a new AuthenticationException.** @param cause the underlying Throwable that caused this exception to be thrown.*/public AuthenticationException(int code, Throwable cause) {super(cause);this.code = code;}/*** Constructs a new AuthenticationException.** @param message the reason for the exception* @param cause   the underlying Throwable that caused this exception to be thrown.*/public AuthenticationException(int code, String message, Throwable cause) {super(message, cause);this.code = code;}}

七、添加全局异常处理类


import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** 全局异常处理** @author kou*/
@ControllerAdvice
public class GlobalException {/*** 授权登录异常*/@ResponseBody@ExceptionHandler(AuthenticationException.class)public BaseResult authenticationException(AuthenticationException e) {return new BaseResult(SystemParameters.FAIL.getIndex(), e.getMessage(), e.getCode());}@ExceptionHandler(Exception.class)@ResponseBodyBaseResult exception(Exception e) {return new BaseResult(SystemParameters.FAIL.getIndex(), e.getMessage(), SystemParameters.ERROR.getIndex());}}

注意:

     由于使用filter过滤器,springmvc 不能进行filter拦截异常,故通过请求转发来实现统一异常拦截

八、错误异常处理,用来处理filter转发出来的请求拦截异常,进而让springmvc统一处理异常

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;/*** 错误控制器** @author kou*/
@RestController
public class ErrorController {@RequestMapping("/exception/authentication")public BaseResult authenticationException(HttpServletRequest request) {throw new AuthenticationException(Integer.valueOf(request.getAttribute("exceptionCode").toString()), request.getAttribute("exceptionMessage").toString());}
}

九、返回统一格式


public class BaseResult {// 结果状态码private int ret;// 结果说明private String msg;private int err;public BaseResult() {}public BaseResult(int ret, String msg, int err) {this.ret = ret;this.msg = msg;this.err = err;}public int getRet() {return ret;}public void setRet(int ret) {this.ret = ret;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public int getErr() {return err;}public void setErr(int err) {this.err = err;}}

springboot 配置 jjwt相关推荐

  1. springboot配置Redis哨兵主从服务 以及 Redis 集群

    redis哨兵集群配置 Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从 ...

  2. SpringBoot配置属性之NOSQL

    SpringBoot配置属性系列 SpringBoot配置属性之MVC SpringBoot配置属性之Server SpringBoot配置属性之DataSource SpringBoot配置属性之N ...

  3. springboot配置Druid数据源

    springboot配置druid数据源 Author:SimpleWu springboot整合篇 前言 对于数据访问层,无论是Sql还是NoSql,SpringBoot默认采用整合SpringDa ...

  4. aop springboot 传入参数_java相关:springboot配置aop切面日志打印过程解析

    java相关:springboot配置aop切面日志打印过程解析 发布于 2020-3-31| 复制链接 摘记: 这篇文章主要介绍了springboot配置aop切面日志打印过程解析,文中通过示例代码 ...

  5. SpringBoot配置属性之Server

    SpringBoot配置属性系列 SpringBoot配置属性之MVC SpringBoot配置属性之Server SpringBoot配置属性之DataSource SpringBoot配置属性之N ...

  6. 使用SpringBoot配置了 server.servlet.path后无效的解决方案

    使用SpringBoot配置了 server.servlet.path后无效的解决方案 参考文章: (1)使用SpringBoot配置了 server.servlet.path后无效的解决方案 (2) ...

  7. SpringBoot配置属性之MQ

    SpringBoot配置属性系列 SpringBoot配置属性之MVC SpringBoot配置属性之Server SpringBoot配置属性之DataSource SpringBoot配置属性之N ...

  8. springboot配置cxf

    1.引入两个需要的jar <dependency><groupId>org.apache.cxf</groupId><artifactId>cxf-rt ...

  9. SpringBoot配置属性之DataSource

    SpringBoot配置属性系列 SpringBoot配置属性之MVC SpringBoot配置属性之Server SpringBoot配置属性之DataSource SpringBoot配置属性之N ...

最新文章

  1. 学习练习SQL的数据库employee文件
  2. 解决:安装SQl 2008为SQL Server代理服务提供的凭据无效
  3. [2018.12.26]BZOJ1022 [SHOI2008]小约翰的游戏John
  4. 学的php毫无兴趣,培训班学PHP,感觉兴趣越来越低,哎,真的很迷茫了!
  5. sqlserver修改字段长度语句_SQL Server读懂语句运行 (三) SET STATISTICS PROFILE ON
  6. 【Calcite】Calcite 的SQL解析
  7. LVS小型系统架构搭建笔记
  8. JBoss的部署机制
  9. CPU负载均衡之loadavg计算
  10. 强大的火狐插件(转)
  11. js定义对象时属性名是否加引号问题
  12. Word导出PDF出现空白页
  13. 信息安全导论知识点梳理
  14. oracle的userenv和nls_lang详解
  15. 全面保护个人电脑中的宝贵数据和文件(转)
  16. 如何删除桌面的回收站图标
  17. 8253工作方式区别、计数初值及应用
  18. 新技术带来产业格局周期波动的3个阶段(以智能汽车领域为例分析)
  19. 阿里云,华为云哪个好?
  20. PyTorch学习系列教程:构建一个深度学习模型需要哪几步?

热门文章

  1. 春节活动设计哪家强?(盘点2020春节红包活动)
  2. Android 浅尝Tinker微信热修复
  3. matlab计算信号得频谱,用MATLAB分析离散信号的频谱与信号的采样
  4. java学生考勤系统视频_手把手教你做一个Java 学生信息、选课、签到考勤、成绩管理系统附带完整源码及视频开发教程...
  5. 【微信小程序】反编译
  6. Java线程池七个参数详解
  7. 爱因斯坦谜题:谁养鱼(C#版)续
  8. linux下find、grep命令详解
  9. python进度条tqdm
  10. Unity 换装系统