概述

JWT,Java Web Token,通过 JSON 形式作为 Web 应用中的令牌,用于在各方之间安全地将信息作为 JSON 对象传输,在数据传输过程中还可以完成数据加密、签名等相关处理

JWT 的作用如下:

  • 授权:一旦用户登录,每个后续请求将包括 JWT,从而允许用户访问该令牌允许的路由,服务和资源
  • 信息交换:JSON Web Token 是在各方之间安全地传输信息的好方法,因为可以对 JWT 进行签名,此外,由于签名是使用标头和有效负载计算的,因此还可以验证内容是否篡改

传统的 Session 认证

1. 认证方式

http 协议本身是一种无状态协议,这就意味着如果用户向我们的应用提供了用户名和密码进行认证,那么下次请求时还需再作一次认证。因为根据 http 协议,我们并不知道是哪个用户发出的请求,所以为了让应用能识别是哪个用户发出的请求,我们只能在服务存储一份用户登录的信息,这份登录信息会在响应传递给浏览器,告诉其保存为 cookie,以便下次请求时发送回来,这样我们就能识别请求来自哪个用户了

2. 缺点

  • 每个用户经过认证后,都要在服务端做一次记录,以方便下次鉴别。通常而言,session 都是保存在内存中的,随着认证用户的增多,服务端的开销也会明显增大
  • 用户认证之后,服务端做认证记录,如果认证的记录被保存到内存中,就意味着下次用户请求还得访问该服务器,才能拿到授权的资源,在分布式应用上,相应的限制了负载均衡器的能力,也就意味着限制了应用的扩展能力
  • 基于 cookie 进行用户识别,cookie 一旦被捕获,用户就会很容易受到跨站请求伪造的攻击
  • 在前后端分离时增加了部署的复杂性

JWT 认证

1. 认证方式

  • 前端通过 Web 表单将自己的用户名和密码发送给后端的接口,这一过程一般是 http post 请求,建议的方式是通过 SSL 加密的传输(https),从而避免敏感信息被嗅探
  • 后端核对用户名和密码成功后,将用户的 id 等其他信息作为 JWT Payload(负载),将其与头部分别进行 Base64 编码,拼接后签名,形成一个 JWT Token
  • 后端将 JWT 字符串作为登录成功的返回结果返回,前端可以将返回的结果保存在本地缓存上,退出登录时前端删除保存的 JWT 即可
  • 前端在每次请求后端带回 JWT
  • 后端检查是否存在,如验证 JWT 的有效性,检查签名是否正确,检查 Token 是否过期,检查 Token 接收方是否是自己
  • 验证通过后,后端使用 JWT 中包含的用户信息进行其他逻辑操作,返回相应结果

2. 优点

  • 简洁,可以通过 URL、POST 参数或者在 HTTP Header 发送,因为数据量小,传输速度快
  • 自包含,负载中包含了所有用户需要的信息,避免多次查询数据库
  • Token 以加密形式保存在客户端,原则上任何 web 形式都支持
  • 不需要在服务端保存会话信息,特别适用于分布式微服务

JWT 结构

通常 JWT 如下所示:xxxxx.yyyyy.zzzzz

  • 标头(Header):标头一般由两部分组成:令牌的类型和所使用的签名算法,标头使用 Base64 编码

    {
    "alg":"HS256",
    "typ":"JWT"
    }
  • 有效负载(payload):令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户)和其他数据的声明。同样使用 Base64 编码。不建议放入用户的敏感信息

    {
    "sub":"12345678",
    "name":"john",
    "admin":true,
    ...
    }
  • 签名(Signature):Signature 需要使用编码后的 Header 和 Payload 以及我们提供的一个密钥,然后使用 Header 中指定签名算法,进行签名。签名的作用是保证 JWT 没有被篡改过,如:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

客户端收到服务端发送的 token 后,再次请求服务端需要带上 token,此时 token 包含三部分:经过 Base64 编码的 Header、经过 Base64 编码的 Payload 和加密后的签名,服务端用自己保存的 secret 与客户端发送的 Header、Payload 运算,如果结果和客户端带回来的签名不一致,则验证失败

JWT 使用

引入依赖

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>

生成 token

Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND, 20);
Map<String, Object> map = new HashMap<>();
String token = JWT.create().withHeader(map) // header
.withClaim("userId", 21) // payload
.withClaim("username", "yeeq")
.withExpiresAt(instance.getTime()) // 指定令牌的过期时间
.sign(Algorithm.HMAC256("FAWF2#!F@")); // 签名,并指定密钥

根据令牌和签名解析数据

// 创建验证对
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("FAWF2#!F@")).build();
// 解码后的信息
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDAyNTQ4NzIsInVzZXJJZCI6MjEsInVzZXJuYW1lIjoieWVlcSJ9.jo_6gKThSXUcEfH1e9bu7at9lm2zmdupwiYvMUWopls");
System.out.println(verify.getClaim("userId").asInt());
System.out.println(verify.getClaim("username").asString());

常见异常信息:

  • SignatureVerificationException:签名不一致异常
  • TokenExpiredException:令牌过期异常
  • AlgorithmMismatchException:算法不匹配异常
  • InvalidClaimException:失效的 payload 异常

JWT 封装工具类

一般结合拦截器或者网关完成认证

public class JWTUtils {

    private static final String SIGN = "FAWF2#!F@";

    /**
* 生成 token
* @param map payload 的信息
* @return token
*/
public static String getToken(Map<String, String> map) {
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7);
JWTCreator.Builder builder = JWT.create();
// 创建 payload
map.forEach((k, v) -> {
builder.withClaim(k, v);
});
String token = builder.withExpiresAt(instance.getTime()) // 指定令牌的过期时间
.sign(Algorithm.HMAC256(SIGN)); // 签名,并指定密钥
return token;
} /**
* 验证 token 的合法性
* @param token token
*/
public static void verifyJWT(String token) {
JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
} /**
* 获取 token 的信息
* @return token 的信息
*/
public static DecodedJWT getTokenInfo(String token) {
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
return verify;
}
}

jwt 学习笔记的更多相关文章

  1. golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息

    golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放 ...

  2. golang学习笔记7 使用beego swagger 实现API自动化文档

    golang学习笔记7 使用beego swagger 实现API自动化文档 API 自动化文档 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/doc ...

  3. 【原创】SpringBoot & SpringCloud 快速入门学习笔记(完整示例)

    [原创]SpringBoot & SpringCloud 快速入门学习笔记(完整示例) 1月前在系统的学习SpringBoot和SpringCloud,同时整理了快速入门示例,方便能针对每个知 ...

  4. Spring Security + JWT学习

    开胃:Oauth2认证流程分析 现在第三方登录已经很普遍了,随便哪个App都会有使用微信登录,使用手机号码登录,或者使用支付宝登录等功能... 下面我们就以使用微信登录,做一个简单的流程分析分析 开胃 ...

  5. SpringBoot学习笔记(十五:OAuth2 )

    @ 目录 一.OAuth 简介 1.什么是OAuth 2.OAuth 角色 3.OAuth 授权流程 4.OAuth授权模式 4.1.授权码 4.2.隐藏式 4.3.密码式 4.4.凭证式 二.实践 ...

  6. 图灵学院JAVA互联网架构师专题学习笔记

    图灵学院JAVA互联网架构师专题学习笔记 下载链接:链接: https://pan.baidu.com/s/1xbxDzmnQudnYtMt5Ce1ONQ 密码: fbdj如果失效联系v:itit11 ...

  7. Spring Cloud微服务学习笔记

    Spring Cloud微服务学习笔记 SOA->Dubbo 微服务架构->Spring Cloud提供了一个一站式的微服务解决方案 第一部分 微服务架构 1 互联网应用架构发展 那些迫使 ...

  8. SpringBoot + Security学习笔记

    SpringSecurity学习笔记 本以为是总结,最后写成了笔记,所以还是需要更加的努力啊. 开始的时候看了一遍官方文档,然后只看懂了加密器. 然后又学了一个尚硅谷的视频,虽然这个教程是在讲一个项目 ...

  9. Vue学习笔记-rest_framework_jwt 学习

    一  使用环境 开发系统: windows 后端IDE: PyCharm 前端IDE: VSCode 数据库: msyql,navicat 编程语言: python3.7  (Windows x86- ...

  10. Vue学习笔记-Django REST framework3后端接口API学习

    一  使用环境 开发系统: windows 后端IDE: PyCharm 前端IDE: VSCode 数据库: msyql,navicat 编程语言: python3.7  (Windows x86- ...

随机推荐

  1. servlet 500,405错误

    servlet500错误 状况: 1.能打开项目,但是打开web.xml中指定的servlet url错误报错500,再次刷新出现404 出现提示: 实例化Servlet类[xxx]异常 java.l ...

  2. Vscode,php运行

    1.下载好vscode,点击左侧扩展,然后搜索php,安装插件 2.打开小皮面板创建网站 点击确认 创建成功 3.浏览器输入http://myblog验证 4.在vscode打开新建的myblog文件 ...

  3. 前端自动化测试框架Cypress环境搭建

    1. 下载安装node.js(之前的随笔里写过安装nodejs具体步骤这里不过多介绍了) 下载地址:https://nodejs.org/en/ 2.安装路径(这是安装完成nodejs的路径) 3. ...

  4. server2008R2 安装.net framework 4.7 4.8 时间戳签名和/或证书无法验证或格式错误

    安装补丁 KB4474419 和KB4490628 实测有效 补丁下载: https://www.catalog.update.microsoft.com/Search.aspx?q=4474419 ...

  5. 20201003--统计数字字符个数(奥赛一本通 P98 1--字符类型和字符数组)

    输入一行字符,统计出其中数字字符的个数 输入:一行字符串,总长度不超过255 输出:一行,输出字符串里面数字字符的个数. 样例输入:Peking University is set up at 189 ...

  6. this和箭头函数的this

    https://www.cnblogs.com/lfri/p/11872696.html https://www.ruanyifeng.com/blog/2018/06/javascript-this ...

  7. OpenStack 云主机ping通外网

  8. Java中double保留2位小数(精度丢失)的两种方式

    Java中double保留2位小数(精度丢失)的两种方式 在我们日常开发中,使用double数据类型进行计算,偶尔会出现精度丢失的情况,例如实际结果是0.75,就可能出现0.7500000000000 ...

  9. Day02 差点水掉 欸呀呀

    Java狂神6.17星期四 知识行 冯诺依曼+图灵 软件+硬件 .......... 快捷键 ctrl+a 全选 ctrl+x 剪切 alt+F4 关闭窗口 win+r 运行 +cmd命令行 win+ ...

  10. sql server 计算时间差的一部分函数【转】

    在做Sql Server开发的时候有时需要获取表中今天.昨天.本周.上周.本月.上月等数据,这时候就需要使用DATEDIFF()函数及GetDate()函数了.DATEDIFF ( datepart ...