JWT结构详解与JWT设置

2024-08-29 20:52
文章标签 设置 详解 结构 jwt

本文主要是介绍JWT结构详解与JWT设置,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

JWT结构详解与JWT设置

  • 1. 什么是token
  • 2. 为什么要使用token
  • 3. 什么是JWT
  • 4. JWT的格式
    • 4.1 header
    • 4.2 payload
    • 4.3 signature
  • 5. JWT校验流程
  • 6. JWT使用案例
    • 6.1 token的创建
    • 6.2 判断token是否可以刷新
    • 6.3 刷新token
    • 6.4 token的校验
    • 6.5 用户验证流程

1. 什么是token

Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务端生成一个token并返回给客户端,以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。

2. 为什么要使用token

大家可能会想到,用服务器的session_id存储到cookies中也能做到,为什么非要用token呢?
个人觉得,开发web应用的话用哪种都行。但如果是开发api接口,前后端分离,最好使用token,因为 session + cookies 是基于web的。但是针对 api接口,需要考虑到移动端,app是没有cookies和session的。

3. 什么是JWT

JWT是 JSON Web Tokens 的简称,从单词可以看出它也是一种 token,其实可以理解为一种生成token的框架或规范。

4. JWT的格式

在这里插入图片描述

4.1 header

在这里插入图片描述
非常简单,typ顾名思义就是type的意思,例如上面这里就指明是JWT的类型。alg顾名思义是algorithm的意思,指代一个加密算法,例如上面指代HS256(HMAC-SHA256),这个算法会在生成第三部分signature的时候用到。

4.2 payload

payload 用来承载要传递的数据,它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为 claims
payload 的一个“属性值对”其实就是一个claim(要求),每一个 claim 都代表特定的含义和作用。
根据JWT的标准,这些 claims 可以分为以下三种类型:

  • Reserved claims(保留)
    它属于JWT标准里面规定的一些claim,就像是编程语言的保留字一样。JWT标准里面定义好的claim有:

    iss(Issuser):代表这个JWT的签发主体;
    sub(Subject):代表这个JWT的主体,即它的所有人;
    aud(Audience):代表这个JWT的接收对象;
    exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
    nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
    iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
    jti(JWT ID):是JWT的唯一标识。

  • Public claims,略(不重要)

  • Private claims(私有)
    这个指的就是自定义的claim。这些claim跟JWT标准规定的claim区别在于:

    • JWT规定的claim,JWT的接收方在拿到JWT之后,都知道怎么对这些标准的claim进行验证(即JWT的每个实现库都会参照这个描述来提供JWT的验证实现);
    • 而private claims不会验证,除非明确告诉接收方要对这些claim进行验证以及规则才行;
    • 按照JWT标准的说明:保留的claims都是可选的,在生成payload不强制用上面的那些claim;

4.3 signature

在这里插入图片描述
signature顾名思义就是签名,签名一般就是用一些算法生成一个能够认证身份的字符串,具体算法就是上面表示的,也比较简单,唯一说明的一点是上面 hash 方法用到了一个 secret,这个东西需要 客户端 和 服务端 双方都知道,相当于约好了同一把验证的钥匙,最终才好做认证。

最后,每个部分都做一个 base64url encoded 的转换,然后按照 header.payload.signature 这个格式串起来就行了

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM

注意:JWT不保证数据不泄露,因为JWT的设计目的就不是数据加密和保护,而是为了认证来源。

5. JWT校验流程

最后再解释一下服务端 如何认证用户发来的JWT是否合法。

首先服务端和客户端必须要有个约定,例如双方同时知道加密用的 secret(这里假设用的就是简单的对称加密算法),那么在服务端 收到这个JWT时,就可以利用 JWT 前两段数据作为输入,用同一套 hash 算法和同一个 secret 自己计算一个签名值,然后把计算出来的签名值和收到的 JWT 第三段比较,如果相同则认证通过,如果不相同,则认证不通过。

就这么简单,当然,上面是假设了这个hash算法是对称加密算法,其实如果用非对称加密算法也是可以的,比方说我就用非对称的算法,对应的密钥就是一对,而非一个,那么一对公钥+私钥可以这样分配:私钥由客户端保存,公钥由服务端保存,服务端验证的时候,用公钥解密收到的 signature,这样就得到了 header 和 payload 的拼接值,用这个拼接值跟前两段比较,相同就验证通过。

6. JWT使用案例

6.1 token的创建

   /*** 初始化生成token的参数* @param userId* @return String*/public String generateToken(String userId) {Map<String, Object> claims = new HashMap<>(1);claims.put("sub", userId);return generateToken(claims);}/*** 生成token* @param claims* @return String*/private String generateToken(Map<String, Object> claims) {return Jwts.builder().setClaims(claims).setExpiration(this.generateExpirationDate()).setIssuedAt(this.generateCurrentDate()).signWith(SignatureAlgorithm.HS512, this.secret).compact();}

6.2 判断token是否可以刷新

    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {Claims claims;try {claims = Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token).getBody();final Date iat = claims.getIssuedAt();final Date exp = claims.getExpiration();if (iat.before(lastPasswordReset) || exp.before(generateCurrentDate())) {return false;}return true;} catch (Exception e) {return false;}}

6.3 刷新token

public String refreshToken(String token) {String refreshedToken;try {final Claims claims = Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token).getBody();refreshedToken = this.generateToken(claims);} catch (Exception e) {refreshedToken = null;}return refreshedToken;}

6.4 token的校验

    public String verifyToken(String token) {String result = "";Claims claims;try {claims = Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token).getBody();result = TokenStatus.TOKEN_VALID;} catch (Exception e) {result = TokenStatus.TOKEN_INVALID;}return result;}

6.5 用户验证流程

用户登录

    @RequestMapping(value = "/login", method = RequestMethod.POST)@ResponseBodypublic JSONResponse login(@RequestBody Map<String, String> map) {String loginName = map.get("loginName");String password = map.get("password");User user1 = new User();user1.setName(loginName);user1.setPassword(password);//身份验证是否成功boolean isSuccess = userService.checkUser(user1);if (isSuccess) {User user = userService.getUserByLoginName(loginName);if (user != null) {//生成token,返回给客户端String token = jwtUtil.generateToken(user.getId());if (token != null) {return JSONResponse.ok(token);}}}//返回登陆失败消息return JSONResponse.info("登陆失败");}

拦截器验证

 @Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;String token = request.getHeader("access_token");//token是否存在if (null != token) {//验证token是否正确String result = jwtUtil.verifyToken(token);if(result.equals(TokenStatus.TOKEN_INVALID)){//无效outputStream(servletResponse,"token令牌无效...");}else{//有效令牌,需要重新刷新token,再将token传回客户端,客户端会拿着新的token进行访问String refreshedToken = jwtUtil.refreshToken(token);System.out.println("refreshedToken:"+refreshedToken);// Access-Control-Allow-Origin就是我们需要设置的域名// Access-Control-Allow-Headers跨域允许包含的头。// Access-Control-Allow-Methods是允许的请求方式
//                response.setHeader("Access-Control-Allow-Origin", "*");// *,任何域名
//                response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");// response.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type, Accept");// 允许请求头Token// httpResponse.setHeader("Access-Control-Allow-Headers","Origin,X-Requested-With, Content-Type, Accept, Token");// 允许客户端,发一个新的请求头jwt
//                response.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With, Content-Type, Accept, jwt");// 允许客户端,处理一个新的响应头jwt
//                response.setHeader("Access-Control-Expose-Headers", "jwt");response.setHeader("access_token", refreshedToken);}filterChain.doFilter(request, response);return;}outputStream(servletResponse,"无token令牌...");}/*** @description: 向客户端返回响应信息(json格式)* @author wangdong* @date 2019/10/8 16:46*/private void outputStream(ServletResponse servletResponse,String message){try{String string = JSON.toJSONString(JSONResponse.info(message));servletResponse.setContentType("application/json;charset=UTF-8");servletResponse.getOutputStream().write(string.getBytes("UTF-8"));servletResponse.getOutputStream().close();}catch (Exception e){e.printStackTrace();}}

--------over

这篇关于JWT结构详解与JWT设置的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1118870

相关文章

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

Python函数作用域示例详解

《Python函数作用域示例详解》本文介绍了Python中的LEGB作用域规则,详细解析了变量查找的四个层级,通过具体代码示例,展示了各层级的变量访问规则和特性,对python函数作用域相关知识感兴趣... 目录一、LEGB 规则二、作用域实例2.1 局部作用域(Local)2.2 闭包作用域(Enclos

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分