JWT
JWT(JSON Web Token)是为了在网络应用环境间传递申明而执行的一种基于JSON的凋谢规范。
举例登录过程
在这里集体整顿了一些材料,有须要的敌人能够间接点击支付。
Java基础知识大全
22本Java架构师外围书籍
从0到1Java学习路线和材料
1000+道2021年最新面试题
组成
JWT具体长什么样呢?
JWT是由三段信息形成的,将这三段信息文本用.链接一起就形成了JWT字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
元素
header
JWT的头部承载两局部信息:
- 申明类型,这里是JWT;
- 申明加密的算法,通常间接应用 HMAC SHA256;
残缺的头部就像上面这样的JSON:
{ 'typ': 'JWT', 'alg': 'HS256'}
应用base64加密,形成了第一局部。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload(重点)
载荷就是寄存无效信息的中央,这些无效信息蕴含三个局部:
- 规范中注册的申明;
- 公共的申明;
- 公有的申明;
其中,规范中注册的申明 (倡议但不强制应用)包含如下几个局部 : - iss: jwt签发者;
- sub: jwt所面向的用户;
- aud: 接管jwt的一方;
- exp: jwt的过期工夫,这个过期工夫必须要大于签发工夫;
- nbf: 定义在什么工夫之前,该jwt都是不可用的;
- iat: jwt的签发工夫;
- jwt的惟一身份标识,次要用来作为一次性token,从而回避重放攻打;
公共的申明局部:公共的申明能够增加任何的信息,个别增加用户的相干信息或其余业务须要的必要信息,但不倡议增加敏感信息,因为该局部在客户端可解密。
公有的申明局部:公有申明是提供者和消费者所独特定义的申明,个别不倡议寄存敏感信息,因为base64是对称解密的,意味着该局部信息能够归类为明文信息。
定义一个payload:
{ "sub": "1234567890", "name": "John Doe", "admin": true}
而后将其进行base64加密,失去Jwt的第二局部:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
jwt的第三局部是一个签证信息,这个签证信息由三局部组成:
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, '密钥');加密之后,失去signature签名信息。TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
jwt最终格局
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
secret用来进行jwt的签发和jwt的验证,所以,在任何场景都不应该表露进来。
元素
SpringBoot整合JWT【正片】
引入依赖
<!--token--> <!-- jwt反对 --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> </dependency>
创立JWT工具类
留神动态属性的配置文件注入形式:
package com.neuq.common.util;import com.auth0.jwt.JWT;import com.auth0.jwt.algorithms.Algorithm;import com.auth0.jwt.exceptions.TokenExpiredException;import com.neuq.common.exception.ApiException;import com.neuq.common.response.ResultInfo;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;import java.util.Date;/** * @Author: xiang * @Date: 2021/5/11 21:11 * <p> * JwtToken生成的工具类 * JWT token的格局:header.payload.signature * header的格局(算法、token的类型),默认:{"alg": "HS512","typ": "JWT"} * payload的格局 设置:(用户信息、创立工夫、生成工夫) * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) */@Component@ConfigurationProperties(prefix = "jwt")public class JWTUtils { //定义token返回头部 public static String header; //token前缀 public static String tokenPrefix; //签名密钥 public static String secret; //有效期 public static long expireTime; //存进客户端的token的key名 public static final String USER_LOGIN_TOKEN = "USER_LOGIN_TOKEN"; public void setHeader(String header) { JWTUtils.header = header; } public void setTokenPrefix(String tokenPrefix) { JWTUtils.tokenPrefix = tokenPrefix; } public void setSecret(String secret) { JWTUtils.secret = secret; } public void setExpireTime(int expireTimeInt) { JWTUtils.expireTime = expireTimeInt*1000L*60; } /** * 创立TOKEN * @param sub * @return */ public static String createToken(String sub){ return tokenPrefix + JWT.create() .withSubject(sub) .withExpiresAt(new Date(System.currentTimeMillis() + expireTime)) .sign(Algorithm.HMAC512(secret)); } /** * 验证token * @param token */ public static String validateToken(String token){ try { return JWT.require(Algorithm.HMAC512(secret)) .build() .verify(token.replace(tokenPrefix, "")) .getSubject(); } catch (TokenExpiredException e){ throw new ApiException(ResultInfo.unauthorized("token曾经过期")); } catch (Exception e){ throw new ApiException(ResultInfo.unauthorized("token验证失败")); } } /** * 查看token是否须要更新 * @param token * @return */ public static boolean isNeedUpdate(String token){ //获取token过期工夫 Date expiresAt = null; try { expiresAt = JWT.require(Algorithm.HMAC512(secret)) .build() .verify(token.replace(tokenPrefix, "")) .getExpiresAt(); } catch (TokenExpiredException e){ return true; } catch (Exception e){ throw new ApiException(ResultInfo.unauthorized("token验证失败")); } //如果残余过期工夫少于过期时常的个别时 须要更新 return (expiresAt.getTime()-System.currentTimeMillis()) < (expireTime>>1); }}
yaml属性配置
jwt: header: "Authorization" #token返回头部 tokenPrefix: "Bearer " #token前缀 secret: "qwertyuiop7418520" #密钥 expireTime: 1 #token无效工夫 (分钟) 倡议一小时以上
登录办法将用户信息存入token中返回
@Override public Map<String,Object> login(User user) { //phone是除id外的惟一标记 须要进行查看 if (user.getPhone() == null || user.getPhone().equals("")) throw new ApiException("手机号不非法"); User selectUser = userDao.selectUserByPhone(user.getPhone()); if (selectUser == null) { //注册用户 int count = userDao.insertUser(user); if (count < 1) throw new ApiException(ResultInfo.serviceUnavailable("注册异样")); } //将userId存入token中 String token = JWTUtils.createToken(selectUser.getUserId().toString()); Map<String,Object> map = new HashMap<>(); map.put("user",selectUser); map.put("token",token); return map; }
留神将token保留到Http 的 header
@GetMapping("/login") public ResultInfo login(User user, HttpServletResponse response) { Map<String, Object> map = userService.login(user); //将token存入Http的header中 response.setHeader(JWTUtils.USER_LOGIN_TOKEN, (String) map.get("token")); return ResultInfo.success((User)map.get("user")); }
拦截器验证每次申请的token
/** * @Author: xiang * @Date: 2021/5/7 20:56 * <p> * 拦截器:验证用户是否登录 */public class UserLoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //http的header中取得token String token = request.getHeader(JWTUtils.USER_LOGIN_TOKEN); //token不存在 if (token == null || token.equals("")) throw new ApiException("请先登录"); //验证token String sub = JWTUtils.validateToken(token); if (sub == null || sub.equals("")) throw new ApiException(ResultInfo.unauthorized("token验证失败")); //更新token无效工夫 (如果须要更新其实就是产生一个新的token) if (JWTUtils.isNeedUpdate(token)){ String newToken = JWTUtils.createToken(sub); response.setHeader(JWTUtils.USER_LOGIN_TOKEN,newToken); } return true; }}
@Configuration@ComponentScan(basePackages = "com.neuq.common") //全局异样解决类须要被扫描能力public class WebMvcConfig implements WebMvcConfigurer { /** * 注册自定义拦截器 * * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserLoginInterceptor()) .addPathPatterns("/user/**") .addPathPatterns("/userInfo/**") .excludePathPatterns("/user/login");//凋谢登录门路 }}
单点登录
将token或者一个惟一标识UUID=UUID.randomUUID().toString()存进Cookie中(别存在Http的header中了),设置门路为整个我的项目根门路/*;
往往以这个惟一标识为key,用户信息为value缓存在服务器中!!!
最初
感激大佬能看到最初,感觉文章对你有帮忙记得点个赞!