Mrli
别装作很努力,
因为结局不会陪你演戏。
Contacts:
QQ博客园

JWT使用学习

2021/12/05
Word count: 3,109 | Reading time: 12min

JWT: Json Web Token

  • 与普通Token一样都是访问资源的令牌,都可以记录用户信息,都是只有验证成功后才可以获取信息。
  • 不同的是普通Token,服务端验证token信息时要进行数据库查询操作;JWT验证token信息就不用,在服务端使用密钥校验就可以,不用经过数据库查询。==》它是一个数字签名

JWT信息组成

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:

  • Header

    • Header header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
  • Payload

    • Payload JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。

      • Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        { 
        // 这部分是Registered claims
        "iss": "Online JWT Builder", // 该JWT的签发者
        "iat": 1416797419, // iat(issued at): 在什么时候签发的(UNIX时间)
        "exp": 1448333419, // 什么时候过期,这里是一个Unix时间戳
        "aud": "www.example.com", // 接收该JWT的一方
        "sub": "jrocket@example.com", // 该JWT所面向的用户
        // 这部分是Public claims
        "GivenName": "Johnny",
        "Surname": "Rocket",
        "Email": "jrocket@example.com",
        "Role": [ "Manager", "Project Administrator" ]
        }
      • Public claims : 可以随意定义。

      • Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。 下面是一个例子:

  • Signature

    • 签名秘钥。 为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。

注: 不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

然后对每一部分进行base64加密,再通过.组合即可得到JWT。 因此,一个典型的JWT看起来是这个样子的:xxxxx.yyyyy.zzzzz

JWT网络工作流程

使用场景:

  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。

  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

    https://jwt.io/

JSON Web Tokens是如何工作的

在认证的时候,当用户用他们的凭证成功登录以后,一个JSON Web Token将会被返回。此后,token就是用户凭证了,你必须非常小心以防止出现安全问题。一般而言,你保存令牌的时候不应该超过你所需要它的时间。

无论何时用户想要访问受保护的路由或者资源的时候,用户代理(通常是浏览器)都应该带上JWT,典型的,通常放在**Authorization header(授权头)**中,用Bearer schema。

服务器上的受保护的路由将会检查Authorization header中的JWT是否有效,如果有效,则用户可以访问受保护的资源。如果JWT包含足够多的必需的数据,那么就可以减少对某些操作的数据库查询的需要,尽管可能并不总是如此。

如果token是在授权头(Authorization header)中发送的,那么跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。

这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *

基于Token的身份认证 与 基于服务器的身份认证 的区别:

基于服务器的身份认证方式存在一些问题:

  • Sessions : 每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大。
  • Scalability : 由于Session是在内存中的,这就带来一些扩展性的问题。
  • CORS : 当我们想要扩展我们的应用,让我们的数据被多个移动设备使用时,我们必须考虑跨资源共享问题。当使用AJAX调用从另一个域名下获取资源时,我们可能会遇到禁止请求的问题。
  • CSRF : 用户很容易受到CSRF攻击。

JWT与Session的差异

  • 它们都是存储用户信息;然而,Session是在服务器端的,客户端只有session id,而JWT是在客户端的。
    • Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
  • 基于Token的身份认证是无状态的,服务器或者Session中不会存储任何用户信息,即没有会话的概念

用Token的好处

  • 无状态和可扩展性:Tokens存储在客户端。完全无状态,可扩展。我们的负载均衡器可以将用户传递到任意服务器,因为在任何地方都没有状态或会话信息。
  • 安全:Token不是Cookie。(The token, not a cookie.)每次请求的时候Token都会被发送。而且,由于没有Cookie被发送,还有助于防止CSRF攻击。即使在你的实现中将token存储到客户端的Cookie中,这个Cookie也只是一种存储机制,而非身份认证机制。没有基于会话的信息可以操作,因为我们没有会话!
  • 还有一点,token在一段时间以后会过期,这个时候用户需要重新登录。这有助于我们保持安全。还有一个概念叫token撤销,它允许我们根据相同的授权许可使特定的token甚至一组token无效。

JWT与OAuth的区别

  • OAuth2是一种授权框架 ,JWT是一种认证协议 -无论使用哪种方式切记用HTTPS来保证数据的安全性
  • OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。

优点

  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展

from:五分钟带你了解啥是JWT

附录

几种常用的认证机制

HTTP Basic Auth

HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password,简言之,Basic Auth是配合RESTful API 使用的最简单的认证方式,只需提供用户名密码即可,但由于有把用户名密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。因此,在开发对外开放的RESTful API时,尽量避免采用HTTP Basic Auth

OAuth

OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

这种基于OAuth的认证机制适用于个人消费者类的互联网产品,如社交类APP等应用,但是不太适合拥有自有认证权限管理的企业应用;

Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效;

Token Auth

Token机制相对于Cookie机制又有什么好处呢?

  • 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.
  • 无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
  • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可.
  • 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
  • CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
  • 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多.
  • 不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.
  • 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).

JSON WebToken Auth

使用java库来生成JWT

JJWT

加密生成JWT

解密JWT

JAVA JWT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class JWTUtil {

// 过期时间5分钟
private static final long EXPIRE_TIME = 5*60*1000;

/**
* 校验token是否正确
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}

/**
* 获得token中的信息无需secret解密也能获得
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}

/**
* 生成签名,5min后过期
* @param username 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String username, String secret) {
try {
Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
return null;
}
}
}

JS——jsonwebtoken

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var express = require("express")
var jwt = require("jsonwebtoken")
var app = express()

app.use(express.json())
var secret = "1234565"

app.get("/login", (req, res) => {
var name = req.query.name;
var pass = req.query.pass;

console.log("url: ", req.method, req.url);
console.log("queryString: ", req.query);
console.log("headers: ", req.headers);
console.log("body: ", req.body);

if (name == "mrli" && pass == "aaa123123") {
var token = jwt.sign({ name }, secret)
res.send({ token })
} else {
// res.sendStatus(401)
res.json({ code: 200, msg: "401" })
}
})

app.get("/bearer", (req, res) => {
try {
var token = req.headers.authorization.split(" ")[1];
var decode_data = jwt.verify(token, secret)
console.log(decode_data.name);
res.send("操作成功")
} catch (e) {
res.send(401)
}
})


app.listen(8080)

Author: Mrli

Link: https://nymrli.top/2021/08/11/JWT使用学习/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
学习画好架构图
NextPost >
Shiro使用学习
CATALOG
  1. 1. JWT: Json Web Token
    1. 1.1. JWT信息组成
    2. 1.2. JWT网络工作流程
    3. 1.3. 使用场景:
    4. 1.4. JSON Web Tokens是如何工作的
    5. 1.5. 基于Token的身份认证 与 基于服务器的身份认证 的区别:
    6. 1.6. 优点
  2. 2. 附录
    1. 2.1. 几种常用的认证机制
      1. 2.1.1. HTTP Basic Auth
      2. 2.1.2. OAuth
      3. 2.1.3. Cookie Auth
      4. 2.1.4. Token Auth
      5. 2.1.5. JSON WebToken Auth
    2. 2.2. 使用java库来生成JWT
    3. 2.3. JJWT
      1. 2.3.1. 加密生成JWT
      2. 2.3.2. 解密JWT
    4. 2.4. JAVA JWT
    5. 2.5. JS——jsonwebtoken