JWT的原理和使用

简介: 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token认证机制。Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

JWT
在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token认证机制。Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

传统的session认证
http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。
但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来.

基于session认证所显露的问题
Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

JWT的构成
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
Header
jwt的头部承载两部分信息:
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{

"alg": "HS256"

}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分. eyJhbGciOiJIUzI1NiJ9

Payload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
标准中注册的声明 (建议但不强制使用) :
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个payload:
{

"euid": "GA000001780",
"accountType": "Personal",
"deviceUniqueCode": "Xw7tEIcd5jEDADuW9S5eXKSE",
"accessTokenExpires": 1637949995184,
"accessToken": "1caa2a8e4adb77007c6d97599749db26ba47b14996db8d75f12f9a404ccfe0f5",
"exp": 1639123595,
"jwtExpires": 1639123595184,
"deviceBrand": "HONOR",
"refreshToken": "3cde7a1cf457685d25fe179c853cb022012acd2913d92d7f67748c6d2d3108ab"

}
然后将其进行base64加密,得到JWT的第二部分
eyJldWlkIjoiR0EwMDAwMDE3ODAiLCJhY2NvdW50VHlwZSI6IlBlcnNvbmFsIiwiZGV2aWNlVW5pcXVlQ29kZSI6Ilh3N3RFSWNkNWpFREFEdVc5UzVlWEtTRSIsImFjY2Vzc1Rva2VuRXhwaXJlcyI6MTYzNzk0OTk5NTE4NCwiYWNjZXNzVG9rZW4iOiIxY2FhMmE4ZTRhZGI3NzAwN2M2ZDk3NTk5NzQ5ZGIyNmJhNDdiMTQ5OTZkYjhkNzVmMTJmOWE0MDRjY2ZlMGY1IiwiZXhwIjoxNjM5MTIzNTk1LCJqd3RFeHBpcmVzIjoxNjM5MTIzNTk1MTg0LCJkZXZpY2VCcmFuZCI6IkhPTk9SIiwicmVmcmVzaFRva2VuIjoiM2NkZTdhMWNmNDU3Njg1ZDI1ZmUxNzljODUzY2IwMjIwMTJhY2QyOTEzZDkyZDdmNjc3NDhjNmQyZDMxMDhhYiJ9

Signature
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

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

public String sign(String jwtWithoutSignature) {
    byte[] bytesToSign = jwtWithoutSignature.getBytes(US_ASCII);
    byte[] signature = this.signer.sign(bytesToSign);
    return TextCodec.BASE64URL.encode(signature);

}
得到JWT的第三部分:H4JhbO-QRMx9Nq-_H6NZM02MlTEyUaSMRIr8e2iD1WI
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:

image.png

五、JWT优点
因为json的通用性,所以JWT是可以进行跨语言支持的,JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
它不需要在服务端保存会话信息, 所以它易于应用的扩展
安全相关
不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
保护好secret私钥,该私钥非常重要。
六、JWT使用示例

在pom.xml文件下添加依赖

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.6.0</version>
    </dependency>
</dependencies>

示例代码:

import java.util.Map;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

/**
 * JWTUtils工具类,生成jwt和解析jwt
 * JSON WEB TOKEN 结构组成:
 * (1)Header(头部):包含加密算法,通常直接使用 HMAC SHA256
 * (2)Payload(负载):存放有效信息,比如消息体、签发者、过期时间、签发时间等
 * (3)Signature(签名):由header(base64后的)+payload(base64后的)+secret(秘钥)三部分组合,然后通过head中声明的算法进行加密
 *
 */
public class JwtUtil {

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey generalKey(String stringKey) {
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 创建jwt
     * @param claims 负载
     * @param expirationDate  生成jwt的有效期,单位秒
     * @return jwt token
     * @throws Exception
     */
    public static String createJWT(Map<String,Object> claims, long expirationDate,String secretKey) throws Exception {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey key = generalKey(secretKey);
        JwtBuilder builder = Jwts.builder().setClaims(claims)
                .signWith(signatureAlgorithm, key);
        if (expirationDate >= 0) {
            long nowMillis = System.currentTimeMillis();
            long expMillis = nowMillis + expirationDate*1000;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * 解密jwt,获取实体
     * @param jwt
     */
    public static Claims parseJWT(String jwt,String secretKey) throws ExpiredJwtException, UnsupportedJwtException,
            MalformedJwtException, SignatureException, IllegalArgumentException {
        SecretKey key = generalKey(secretKey);
        Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody();
        return claims;
    }

}
相关文章
|
2月前
|
存储 JSON 算法
无懈可击的身份验证:深入了解JWT的工作原理
无懈可击的身份验证:深入了解JWT的工作原理
77 0
|
5月前
|
JSON 安全 Java
JWT的原理及实际使用
JWT的原理及实际使用
65 0
|
5月前
|
前端开发
什么是JWT?深入理解JWT从原理到应用(下)
什么是JWT?深入理解JWT从原理到应用(下)
31 0
|
6月前
|
存储 JSON 算法
JWT的原理及实际应用
JWT的原理及实际应用
80 1
|
6月前
|
存储 JSON 前端开发
了解什么是JWT的原理及实际应用
了解什么是JWT的原理及实际应用
1114 0
|
3月前
|
JSON 安全 网络安全
超详细的用户认证、权限、安全原理详解(认证、权限、JWT、RFC 7235、HTTPS、HSTS、PC端、服务端、移动端、第三方认证等等)
超详细的用户认证、权限、安全原理详解(认证、权限、JWT、RFC 7235、HTTPS、HSTS、PC端、服务端、移动端、第三方认证等等)
348 0
|
3月前
|
JSON 算法 前端开发
gin框架JWT验证实践(原理介绍,代码实践)
gin框架JWT验证实践(原理介绍,代码实践)
48 0
|
4月前
|
JSON 算法 测试技术
JWT库生成Token的使用与原理
JWT库生成Token的使用与原理
109 0
|
4月前
|
JSON 算法 数据库
JWT(JSON Web Token)的基本原理
JWT(JSON Web Token)的基本原理
|
5月前
|
JSON 算法 前端开发
什么是JWT?深入理解JWT从原理到应用(上)
什么是JWT?深入理解JWT从原理到应用(上)
230 0