快速入门-JSON WEB Token(JWT)

288 阅读10分钟

JSON Web Token(JWT)是一种开放标准 (RFC 7519) 定义的轻量级、安全的数据传输方式,用于在各方之间以 JSON 对象的形式安全地传递信息。它主要用于用户身份验证和授权。例如,服务器可以生成一个带有“以管理员身份登录”声明的令牌,并将其提供给客户端。然后,客户端可以使用该令牌来证明它以管理员身份登录。令牌可以由一方的私钥(通常是服务器的私钥)签名,以便任何一方都可以随后验证令牌是否合法。如果另一方通过某种合适且值得信赖的方式拥有相应的公钥,他们也能够验证令牌的合法性。令牌设计为紧凑、URL安全、和可用,尤其是在Web 浏览器 单点登录(SSO) 环境中。JWT 声明通常可用于在身份提供者和服务提供者之间传递经过身份验证的用户身份,或业务流程所需的任何其他类型的声明。在传统的用户身份认证和授权中,普遍采用基于会话的方式,存在以下问题:1. 服务器需要存储会话状态,增加内存消耗。2. 多台服务器之间共享会话状态复杂。3. 会话机制不适合微服务架构。JWT 的设计满足了现代应用的需求,使身份验证和授权更加灵活高效。
JWT令牌结构:实际上就是一个字符串,它由三部分组成:头部(Header)、载荷(Payload)与签名(signature)

# **J**WT令牌由Header、Payload、Signature三部分组成,每部分中间使用点(.)分隔,比如:Header.Payload.Signature

# 完整 JWT 示例
 
 Header                               Payload                                             Signature
 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MzIwMDc4NDAsInVzZXJuYW1lIjoid2l6YXJkIn0.rzAy5XqcToupKc2Hn8zHx71n4gc2VIPqSaYwOvV-aSE

特点:

  • 轻量化:基于 JSON 格式,便于解析。

  • 安全性:使用签名验证,确保数据完整性。

  • 无状态:无需在服务器端存储会话信息。

  • 可携带:可通过 URL、HTTP 头、Cookie 等方式传输。

基于Java的快速实践

依赖配置:如果使用 Maven,添加以下依赖到 pom.xml:

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

案例代码 - 1.生成 JWT

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;

public class JwtDemo {
    // 签名密钥(需要妥善保管)
    private static final String SECRET_KEY = "my_secret_key";

    public static String generateToken() {
        // 设置过期时间(例如 1 小时后)
        Date expireDate = new Date(System.currentTimeMillis() + 3600 * 1000);

        // 使用 HS256 算法生成 Token
        return JWT.create()
                .withIssuer("example.com")            // 签发者
                .withSubject("user123")               // 用户标识
                .withClaim("username""john_doe")    // 自定义字段
                .withExpiresAt(expireDate)            // 过期时间
                .sign(Algorithm.HMAC256(SECRET_KEY)); // 签名算法
    }

    public static void main(String[] args) {
        String token = generateToken();
        System.out.println("Generated JWT: " + token);
    }
}

案例代码 - 2.验证 JWT

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;

public class JwtVerificationDemo {
    private static final String SECRET_KEY = "my_secret_key";

    public static void verifyToken(String token) {
        try {
            // 创建验证器
            Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);
            JWTVerifier verifier = JWT.require(algorithm) // 指定验证规则
                    .withIssuer("example.com")            // 验证签发者
                    .build();

            // 验证 Token
            DecodedJWT jwt = verifier.verify(token);
            System.out.println("Token is valid!");
            System.out.println("Subject: " + jwt.getSubject());
            System.out.println("Username: " + jwt.getClaim("username").asString());
            System.out.println("Expires At: " + jwt.getExpiresAt());
        } catch (JWTVerificationException e) {
            System.err.println("Invalid Token: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        // 测试验证
        String token = JwtDemo.generateToken(); // 使用之前生成的 Token
        verifyToken(token);
    }
}

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt了。

JWT 详解

基本组成

# **J**WT令牌由Header、Payload、Signature三部分组成,每部分中间使用点(.)分隔,比如:Header.Payload.Signature

# 1.Header(头部)

 {
   "alg""HS256", 声明加密的算法 通常直接使用 HMAC SHA256
   "typ""JWT"    声明类型,这里是jwt
 }

 包括类别(typ)、加密算法(alg);
 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分

# 2.Payload(载荷)

 {
   "sub""1234567890",
   "name""John Doe",
   "admin"true
 }

 载荷就是存放有效信息的地方。这些有效信息包含三个部分
    1.标准中注册的声明
    2.公共的声明
    3.私有的声明

    标准中注册的声明 (建议但不强制使用)
     iss:            该JWT的签发者,一般是服务器,是否使用是可选的;
     iat(issued at): 在什么时候签发的(UNIX时间),是否使用是可选的;
     exp(expires):   什么时候过期,这里是一个Unix时间戳,是否使用是可选的;
     aud:            接收该JWT的一方,是否使用是可选的;
     sub:            该JWT所面向的用户,userid,是否使用是可选的;

    其他还有:
     nbf (Not Before):如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟;,是否使用是可选的;
     jti:              jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

  公共的声明:
  公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

  私有的声明:
  私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息.

# 3. Signature(签名)

 HMACSHA256(  
   base64UrlEncode(header) + "." +
   base64UrlEncode(payload),
   SECREATE_KEY
 )

 // 根据alg算法与私有秘钥进行加密得到的签名字串;
 // SECREATE_KEY 仅仅为保存在服务器中;

 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

JWT流程

 1.用户登录:用户通过用户名和密码进行身份验证。
 2.JWT 生成:验证成功后,服务器生成一个 JWT,包含用户的身份信息(通常是用户 ID、角色等)。
 3.发送 JWT:JWT 被发送到客户端,并且存储在客户端(通常是浏览器的 LocalStorage 或 Cookie)。
 4.访问资源:客户端请求需要授权的资源时,会将 JWT 包含在请求头(Authorization header)中。
 5.JWT 验证:服务器接收到请求后,验证 JWT 的合法性(通过 Signature)。
 6.授权/拒绝:如果 JWT 验证通过,允许访问资源,否则返回错误信息。

总结

JWT 最适合分布式系统、微服务架构和跨平台应用,适用于需要无状态认证的场景。它支持 API 认证、单点登录(SSO)等,但需要采取额外措施确保其安全性(如加密、有效期管理等)。


知识扩展【几种常用的认证机制】

HTTP Basic Auth

在HTTP中,基本认证是一种用来允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式,通常用户名和明码会通过HTTP头传递。
在发送之前是以用户名追加一个冒号然后串接上口令,并将得出的结果字符串再用Base64算法编码。

 
例如
用户名是Aladdin、口令是open sesame,则拼接后的结果就是Aladdin:open sesame,
 
然后再将其用Base64编码,得到QWxhZGRpbjpvcGVuIHNlc2FtZQ==。最终将Base64编码的字符串发送出去,由接收者解码得到一个由冒号分隔的用户名和口令的字符串。
 
优点:基本认证的一个优点是基本上所有流行的网页浏览器都支持基本认证。
缺点:由于用户名和密码都是Base64编码的,而Base64编码是可逆的,所以用户名和密码可以认为是明文。所以只有在客户端和服务器主机之间的连接是安全可信的前提下才可以使用。

OAuth

OAuth 是一个关于授权(authorization)的开放网络标准。允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。现在的版本是2.0版。
严格来说,OAuth2不是一个标准协议,而是一个安全的授权框架。
它详细描述了系统中不同角色、用户、服务前端应用(比如API),以及客户端(比如网站或移动App)之间怎么实现相互认证。

 
名词释义:
Resource Owner:资源所有者,通常称"用户"(user)。

Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
 
Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

 
(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权。

(C)客户端使用上一步获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源。

 
优点:
 快速开发
 实施代码量小
 维护工作减少
 如果设计的API要被不同的App使用,并且每个App使用的方式也不一样,使用OAuth2是个不错的选择。
 
缺点:
 OAuth2是一个安全框架,描述了在各种不同场景下,多个应用之间的授权问题。有海量的资料需要学习,要完全理解需要花费大量时间。
 OAuth2不是一个严格的标准协议,因此在实施过程中更容易出错。

**
**

JWT 认证

Json web token (JWT), 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

JWT 特点

体积小,因而传输速度快
传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输
严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
支持跨域验证,可以应用于单点登录

致谢

更多内容欢迎关注 [ 小巫编程室 ] 公众号,喜欢文章的话,也希望能给小编点个赞或者转发,你们的喜欢与支持是小编最大的鼓励,小巫编程室感谢您的关注与支持。好好学习,天天向上(good good study day day up)。

参考资料[1]

jwt: jwt.io/

java-jwt: github.com/auth0/java-…

json-web-tokens: auth0.com/docs/secure…