我们都知道JWT是一个JSON信息传输的开放标准,它可以使用密钥对信息进行数字签名,以确保信息是可验证和可信任的。今天我们来深入总结SpringBoot整合JWT
前言
我们都知道JWT是一个JSON信息传输的开放标准,它可以使用密钥对信息进行数字签名,以确保信息是可验证和可信任的。今天我们来深入总结SpringBoot整合JWT
举例登录过程
认证流程
- 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议) ,从而避免敏感信息被嗅探。
- 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload (负载) ,将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。形成的JWT就是一 个形同111.zzz . xxx的字符串。 token head. payload . singurater
- 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStor age或sessionStorage上,退出登录时前端删除保存的JWT即可。
- 前端在每次请求时将JWT放入HTTP Header中的Authorization位。 (解决XSS和XSRF问题) HEADER
- 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)
- 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。
jwt优势
- 不需要在服务端保存会话信息,特别适用于分布式微服务。
- 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
- 简洁(Compact):可以通过URL, POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
- 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
组成
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缓存在服务器中!!!
最后
对一个 Java 程序员而言,并发编程能否熟练掌握是判断他是不是优秀的重要标准之一。因为并发编程在 Java 语言中最为晦涩的知识点,它涉及内存、CPU、操作系统、编程语言等多方面的基础能力,更加考验一个程序员的内功深厚程度。
特别是当大数据时代的来临,高并发更加成为了家常便饭,在工作中,我们总是绕不开并发编程的任务。比如说,你想写个程序,一边从文件中读取数据,一边还要做实时计算…所以,想成为一名资深的 Java 后端工程师,并发编程是必须要牢牢把握的。那我们到底应该如何深入学习Java并发编程呢?
推荐一篇文章:作为一名双非本科毕业的Java程序员,我该如何在日益严重的内卷化中避免被裁?
原文转载:http://www.shaoqun.com/a/803573.html
百思买:https://www.ikjzd.com/w/394
photobucket:https://www.ikjzd.com/w/132
马士基集团:https://www.ikjzd.com/w/1296
gem:https://www.ikjzd.com/w/1997
我们都知道JWT是一个JSON信息传输的开放标准,它可以使用密钥对信息进行数字签名,以确保信息是可验证和可信任的。今天我们来深入总结SpringBoot整合JWT前言我们都知道JWT是一个JSON信息传输的开放标准,它可以使用密钥对信息进行数字签名,以确保信息是可验证和可信任的。今天我们来深入总结SpringBoot整合JWT举例登录过程认证流程首先,前端通过Web表单将自己的用户名和密码发送到后
Yeatrade:https://www.ikjzd.com/w/2276
义乌盈和:https://www.ikjzd.com/w/2277
运去哪:https://www.ikjzd.com/w/2278
tiki:https://www.ikjzd.com/w/2053
dojo:https://www.ikjzd.com/w/2052
环球b2b:https://www.ikjzd.com/w/1762
宝贝真紧要被你夹断了 偷听哥哥嫂子的那段不堪事儿:http://lady.shaoqun.com/m/a/274355.html
啊,姨妹,我受不了了 那晚乘老婆出差去了强行上了小姨子:http://www.30bags.com/a/255129.html
30岁的情侣相爱一次,多久比较好?最好不要超过这个数字:http://www.30bags.com/a/383341.html
外贸推广渠道有哪些:https://www.ikjzd.com/articles/145741
速卖通如何去开车:https://www.ikjzd.com/articles/145734
亚马逊有望将在 2022 年超越沃尔玛成为美国最大的零售商:https://www.ikjzd.com/articles/145751
That's the article: 深入总结SpringBoot整合JWT,这应该是全网讲的最通俗易懂的了
You are now reading the article 深入总结SpringBoot整合JWT,这应该是全网讲的最通俗易懂的了 with link address https://socialnetworkingupdate.blogspot.com/2021/06/springbootjwt.html
Mag-post ng isang Komento