JWT简介
Jwt全称是:json web token。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。
优点:
- 简洁: 可以通过URL、POST参数或者在HTTP header发送,因为数据量小,传输速度也很快;
- 自包含:负载中可以包含用户所需要的信息,避免了多次查询数据库;
- 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持;
- 不需要在服务端保存会话信息,特别适用于分布式微服务。
缺点:
- 无法作废已颁布的令牌;
- 不易应对数据过期。
备注:JWT官网
1、Jwt消息构成
1.1组成
一个token分3部分,按顺序为
- 头部(header)
- 载荷(payload)
- 签证(signature)
三部分之间用.号做分隔。例如:
| eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxYzdiY2IzMS02ODFlLTRlZGYtYmU3Yy0wOTlkODAzM2VkY2UiLCJleHAiOjE1Njk3Mjc4OTF9.wweMzyB3tSQK34Jmez36MmC5xpUh15Ni3vOV_SGCzJ8
 | 
Jwt的头部承载两部分信息:
- 声明类型,这里是Jwt
- 声明加密的算法 通常直接使用 HMAC SHA256
Jwt里验证和签名使用的算法列表如下:
| JWS | 算法名称 | 
| HS256 | HMAC256 | 
| HS384 | HMAC384 | 
| HS512 | HMAC512 | 
| RS256 | RSA256 | 
| RS384 | RSA384 | 
| RS512 | RSA512 | 
| ES256 | ECDSA256 | 
| ES384 | ECDSA384 | 
| ES512 | ECDSA512 | 
 
1.3 playload
载荷就是存放有效信息的地方。基本上填2种类型数据
- 标准中注册的声明的数据;
- 自定义数据。
由这2部分内部做base64加密。
- 标准中注册的声明 (建议但不强制使用) | iss: jwt签发者sub: jwt所面向的用户
 aud: 接收jwt的一方
 exp: jwt的过期时间,这个过期时间必须要大于签发时间
 nbf: 定义在什么时间之前,该jwt都是不可用的.
 iat: jwt的签发时间
 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
 
 |  
 
- 自定义数据:存放我们想放在token中存放的key-value值
1.4 signature
Jwt的第三部分是一个签证信息,这个签证信息由三部分组成
base64加密后的header和base64加密后的payload连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了Jwt的第三部分。
SpringBoot集成JWT
1、添加依赖
在pom.xml文件中添加如下依赖:
| <dependency>
 <groupId>com.auth0</groupId>
 <artifactId>java-jwt</artifactId>
 <version>3.10.3</version>
 </dependency>
 
 | 
2、简单配置使用
2.1创建一个token工具类:
创建TokenUtils.java 
| import cn.hutool.core.util.StrUtil;import com.auth0.jwt.JWT;
 import com.auth0.jwt.algorithms.Algorithm;
 import com.qiancheng.springboot.entity.User;
 import com.qiancheng.springboot.service.IUserService;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.util.Date;
 
 @Component
 public class TokenUtils {
 
 private static IUserService staticUserService;
 
 @Resource
 private IUserService userService;
 
 
 
 
 private static final long EXPIRE_TIME = 30 * 60 * 1000;
 
 @PostConstruct
 public void setUserService(){
 staticUserService = userService;
 }
 
 
 
 
 
 
 
 public static String genToken(String userId,String sign) {
 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
 
 return JWT.create().withAudience(userId)
 .withExpiresAt(date)
 .sign(Algorithm.HMAC256(sign));
 }
 
 public static User getCurrentUser(){
 try {
 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
 String token = request.getHeader("token");
 if (StrUtil.isNotBlank(token)){
 String userId = JWT.decode(token).getAudience().get(0);
 return staticUserService.getById(Integer.valueOf(userId));
 }
 }catch (Exception e){
 return null;
 }
 
 return null;
 }
 }
 
 | 
关于@PostConstruct注解参考文章
2.2创建拦截器,拦截token
创建JWTInterceptor.java
| import cn.hutool.core.util.StrUtil;import com.auth0.jwt.JWT;
 import com.auth0.jwt.JWTVerifier;
 import com.auth0.jwt.algorithms.Algorithm;
 import com.auth0.jwt.exceptions.JWTDecodeException;
 import com.auth0.jwt.exceptions.JWTVerificationException;
 import com.qiancheng.springboot.common.Constants;
 import com.qiancheng.springboot.entity.User;
 import com.qiancheng.springboot.exception.ServiceException;
 import com.qiancheng.springboot.service.IUserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerInterceptor;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 
 
 
 public class JWTInterceptor implements HandlerInterceptor {
 
 @Autowired
 private IUserService userService;
 
 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 String token = request.getHeader("token");
 
 if(!(handler instanceof HandlerMethod)){
 return true;
 }
 
 if (StrUtil.isBlank(token)) {
 throw new ServiceException(Constants.CODE_401,"无Token,请重新登录!");
 }
 
 
 String userId;
 try {
 userId = JWT.decode(token).getAudience().get(0);
 } catch (JWTDecodeException j) {
 throw new ServiceException(Constants.CODE_401,"Token验证失败,请重新登录!");
 }
 
 
 User user = userService.getById(userId);
 if (user == null){
 throw new ServiceException(Constants.CODE_401,"用户不存在,请重新登录!");
 }
 
 
 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
 try {
 jwtVerifier.verify(token);
 } catch (JWTVerificationException e) {
 throw new ServiceException(Constants.CODE_401,"Token验证失败,请重新登录!");
 }
 
 return true;
 }
 
 }
 
 
 | 
2.3注册拦截器
创建InterceptorConfig.java
| import com.qiancheng.springboot.config.interceptor.JWTInterceptor;import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 @Configuration
 public class InterceptorConfig implements WebMvcConfigurer {
 
 @Override
 public void addInterceptors(InterceptorRegistry registry) {
 registry.addInterceptor(jwtInterceptor())
 .addPathPatterns("/**")
 .excludePathPatterns("/user/login","/user/register","/**/export","/**/import");
 
 
 
 
 
 
 
 
 
 
 
 
 }
 
 @Bean
 public JWTInterceptor jwtInterceptor(){
 return new JWTInterceptor();
 }
 
 }
 
 
 | 
2.4使用
在返回前端的数据封装类里新增token属性,登录成功时给token属性赋值,之后前端的所有请求都在header里携带token,后端会自动拦截到token后再处理请求。
返回前端的封装类UserDTO.java:
| import lombok.Data;
 
 
 
 @Data
 public class UserDTO {
 
 private String username;
 private String password;
 private String nickname;
 private String avatarUrl;
 
 private String token;
 }
 
 | 
登录成功,给token赋值:
| String token = TokenUtils.genToken(one.getId().toString(),one.getPassword());
 
 userDTO.setToken(token);
 
 |