4.JWT(JSON Web Token,JSON令牌)

目录


Spring Security专栏目录(点击进入…)



背景

在B/S系统中,登录功基本都是依靠Cookie来实现的,用户登录成功之后主要需要客户端和服务端完成以下两项工作:
(1)服务端将登录状态记录到Session中,或者签发Token;
(2)客户端利用Cookie保存于服务端对应的Session ID或Token。之后每次请求都会带上Cookie信息(包含Session ID或Token),当服务端收到请求后,通过验证Cookie中的信息来判断用户是否登录 。

单点登录:单点登录(Single Sign On, SSO)是指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的应用系统。单点登录的本质就是在多个应用系统中共享登录状态。 SSO是目前比较流行的企业业务整合的解决方案之一。

如果用户的登录状态是记录在 Session 中的,要实现共享登录状态,就要先共享 Session,比如可以将 Session 序列化到 Redis 中,让多个应用系统共享同一个 Redis,直接读取 Redis 来获取 Session。当然仅此是不够的,因为不同的应用系统有着不同的域名,尽管 Session 共享了,但是由于 Session ID 是往往保存在浏览器 Cookie 中的,因此存在作用域的限制,无法跨域名传递,也就是说当用户在 app1.com 中登录后,Session ID 仅在浏览器访问 app1.com 时才会自动在请求头中携带,而当浏览器访问 app2.com 时,Session ID 是不会被带过去的。实现单点登录的关键在于,如何让 Session ID(或 Token)在多个域中共享。

目前而言,主要有以下3种方式:

(1)父域Cookie
例如:baike.baidu.com、wenku.baidu.com、zhida.baidu.com可以将认证的cookie放入baidu.com这个父级域名中,从而实现登录信息的共享。此种实现方式比较简单,但不支持跨主域名

(2)认证中心
我们可以部署一个认证中心,认证中心就是一个专门负责处理登录请求的独立的 Web 服务。用户统一在认证中心进行登录,登录成功后,认证中心记录用户的登录状态,并将 Token 写入 Cookie。(注意这个 Cookie 是认证中心的,应用系统是访问不到的。)

应用系统检查当前请求有没有 Token,如果没有,说明用户在当前系统中尚未登录,那么就将页面跳转至认证中心。由于这个操作会将认证中心的 Cookie 自动带过去,因此,认证中心能够根据 Cookie 知道用户是否已经登录过了。如果认证中心发现用户尚未登录,则返回登录页面,等待用户登录,如果发现用户已经登录过了,就不会让用户再次登录了,而是会跳转回目标 URL ,并在跳转前生成一个 Token,拼接在目标 URL 的后面,回传给目标应用系统。

应用系统拿到 Token 之后,还需要向认证中心确认下 Token 的合法性,防止用户伪造。确认无误后,应用系统记录用户的登录状态,并将 Token 写入 Cookie,然后给本次访问放行。(注意这个 Cookie 是当前应用系统的,其他应用系统是访问不到的。)当用户再次访问当前应用系统时,就会自动带上这个 Token,应用系统验证 Token 发现用户已登录,于是就不会有认证中心什么事了。

目前被广泛使用的方案主要是Apereo CAS 。Apereo CAS 是一个企业级单点登录系统,其中 CAS 的意思是”Central Authentication Service“。它最初是耶鲁大学实验室的项目,后来转让给了 JASIG 组织,项目更名为 JASIG CAS,后来该组织并入了 Apereo 基金会,项目也随之更名为 Apereo CAS。

(3)LocalStorage跨域
在前后端分离的情况下,完全可以不使用 Cookie,我们可以选择将 Session ID (或 Token )保存到浏览器的 LocalStorage 中,让前端在每次向后端发送请求时,主动将 LocalStorage 的数据传递给服务端。这些都是由前端来控制的,后端需要做的仅仅是在用户登录成功后,将 Session ID (或 Token )放在响应体中传递给前端。在这样的场景下,单点登录完全可以在前端实现。前端拿到 Session ID (或 Token )后,除了将它写入自己的 LocalStorage 中之外,还可以通过特殊手段将它写入多个其他域下的 LocalStorage 中。


什么是JWT?

JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。

尽管可以对JWT进行加密以在双方之间提供保密性,但将重点关注已签名的令牌。签名的令牌可以验证其中包含的声明的完整性,而加密的令牌则将这些声明隐藏在其他方的面前。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方才是对其进行签名的一方。

现在JSON Web Tokens(JWT)是非常流行的。尤其是Web开发领域。流行、安全、稳定、易用、支持JSON,所有这些因素,令JWT名声大振。

JWT是一种在两方之间传输信息的方法。在JWT的主体中编码的信息被称为claims。JWT的扩展形式是JSON格式,因此每个claim都是JSON对象中的一个键。JWTs可以加密签名(使它成为JWS)或加密(使它成为JWE)。这为JWTs增强了可验证性。例如,接收者可以确定JWT没有通过验证签名来篡改。所生成JWT的结果是有三个部分的字符串,每个部分由“.”分隔;每一节都是base 64编码的

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY

第一部分是header,必须指定用于签署JWT的算法。

eyJhbGciOiJIUzI1NiJ9

第二部分是body。包含了JWT编码的所有声明。

eyJzdWIiOiJKb2UifQ

最后一部分是signature。它通过在头文件中指定的算法通过header和body的组合来计算。

ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY

如果通过一个基本的64解码器传递前两个部分,将得到:

header:
{
  "alg": "HS256"
}

body:
{
  "sub": "Joe"
}

在这种情况下,得到的信息是使用sha - 256算法的HMAC来签署JWT。而且body有一个claim sub与value Joe。Registered Claims包含一些标准的claims,sub就是其中的一个(subject)。要计算签名,必须知道签名的sercrect。


JWT特点

(1)授权

这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用

(2)信息交换

JSON Web令牌是在各方之间安全地传输信息的好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确定发件人是他们所说的人。另外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。

(3)支持跨域访问

Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输

(4)无状态(也称:服务端可扩展行)

Token机制在服务端不需要存储session信息,因为Token自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.

(5)去耦

不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在的API被调用的时候,你可以进行Token生成调用即可

(6)更适用于移动应用

当客户端是一个原生平台(iOS,Android,Windows 8等)时,Cookie是不被支持的(需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。

(9)CSRF

因为不再依赖于Cookie,所以就不需要考虑对CSRF(跨站请求伪造)的防范(如果token是用cookie保存,CSRF还是需要考虑,一般建议使用
①在HTTP请求中以参数的形式加入一个服务器端产生的token
②放入http请求头中也就是一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中)

(10)基于标准化

API可以采用标准化的JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET,Ruby,Java,Python,PHP)和多家公司的支持(如:Firebase,Google,Microsoft)

(11)性能

JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数,请求数据库的一次网络往返时间(通过数据库查询session信息),相对比HMAC SHA256计算Token验证和解析要费时得多


JWT问题和趋势

(1)令牌问题

JWT默认不加密,但可以加密。生成原始令牌后,可以使用该令牌再次对其进行加密

(2)会话状态

JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。

(3)安全问题

JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。而且JWT不建议使用HTTP协议来传输代码,为了减少盗用和窃取,而是使用加密的HTTPS协议进行传输

1.JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密
2.当JWT未加密方法是,一些私密数据无法通过JWT传输
3.JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数
4.JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效
5.JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证
6.为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输

JWT生成token的安全性分析
从JWT生成的token组成上来看,要想避免token被伪造,主要就得看签名部分了,而签名部分又有三部分组成,其中头部和载荷的base64编码,几乎是透明的,毫无安全性可言,那么最终守护token安全的重担就落在了加入的盐上面了!试想:如果生成token所用的盐与解析token时加入的盐是一样的。岂不是类似于中国人民银行把人民币防伪技术公开了?大家可以用这个盐来解析token,就能用来伪造token。这时就需要对盐采用非对称加密的方式进行加密,以达到生成token与校验token方所用的盐不一致的安全效果!

非对称加密RSA介绍
基本原理:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端私钥加密,持有私钥或公钥才可以解密公钥加密,持有私钥才可解密
优点:安全,难以破解
缺点:算法比较耗时,为了安全,可以接受
历史:三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字缩写:RSA。


JWT组成

JSON Web令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔。
JWT通常如下结构:header.playload.signatrue

xxxxx.yyyyy.zzzzz

一个JWT token分3部分,按顺序为
①头部(header)
②其为载荷(payload)
③签证(signature)

(1)头部(header)

Jwt的头部承载两部分信息:

{
	"alg" : "HS256",
	"typ" : "JWT"
}

alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。然后,此JSON被Base64 Url编码以形成JWT的第一部分

①声明类型,这里是jwt
②声明加密的算法 通常直接使用HMAC SHA256
JWT里验证和签名使用的算法,可选择下面的。除下面默认字段外,还可以自定义私有字

JWS算法名称描述
HS256HMAC256HMAC with SHA-256
HS384HMAC384HMAC with SHA-384
HS512HMAC512HMAC with SHA-512
RS256RSA256RSASSA-PKCS1-v1_5 with SHA-256
RS384RSA384RSASSA-PKCS1-v1_5 with SHA-384
RS512RSA512RSASSA-PKCS1-v1_5 with SHA-512
ES256ECDSA256ECDSA with curve P-256 and SHA-256
ES384ECDSA384ECDSA with curve P-384 and SHA-384
ES512ECDSA512ECDSA with curve P-521 and SHA-512

使用代码如下

// header Map
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
Jwts.builder().setHeader(map);

(2)有效载荷(playload)

载荷就是存放有效信息的地方。基本上填2种类型数据
①标准中注册的声明的数据
②自定义数据
由这2部分内部做base64加密。最终数据进入JWT的chaims里存放。

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。JWT指定七个默认字段(claim)供选择。所有的claim是可选的,并且是大小写敏感的

载荷描述
iss“iss” (issuer) Claim是签发该证书的发行人。包含一个StringOrURI值
sub“sub” (Subject)是主体。通常是关于subject的陈述
aud“aud” (Audience) Claim是指jwt的接受者,假如aud没有发现,则解析jwt时会抛出异常
exp“exp” (Expiration Time)指过期时间,假如超过过期时间,则会抛出异常
nbf“nbf” (Not Before) Claim指的是开始日期,claim要求当前日期/时间必须在以后或等于。在“nbf”声明中列出的日期/时间
iat“iat” (Issued At) Claim是指jwt的发行时间
jti“jti” (JWT ID) Claim为JWT提供了JWT ID唯一的标识符,用于标识该JWT;如果应用程序使用多个发行者,必须在值之间避免冲突,由不同的发行商制作。

使用方法

Jwts.builder().setHeader(map) // header
        .setClaim("iss", "Service") // payload
        .setClaim("aud", "APP")
        .setIssuedAt(iatDate) // sign time
        .setExpiresAt(expiresDate) // expire time

自定义数据
这个就比较简单,存放想放在token中存放的key-value值

Jwts.builder().setHeader(map) // header
        .setClaim("name", "cy") // payload
        .setClaim("user_id", "11222");

(3)签名(signature)

JWT的第三部分是一个签证信息,这个签证信息算法如下:

base64UrlEncode(header) + "." + base64UrlEncode(payload) + your-256-bit-secret

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

注意:默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。JSON对象也使用Base64 URL算法转换为字符串保存。

Jwts.builder()
        .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())

签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。
首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。

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

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。

Base64URL算法
JWT Header头和JWT playload有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。

作为令牌的JWT可以放在URL中(例如:api.example/?token=xxx)。 Base64中用的三个字符是“+”、“/”和“=”,由于在URL中有特殊含义,因此Base64URL中对他们做了替换:
①“=”去掉
②“+”用“-”替换
③“/”用“_”替换
这就是Base64URL算法


JWT用法(简单使用)

客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求头的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。

跨域身份验证

Internet服务无法与用户身份验证分开。一般过程如下。

1.用户向服务器发送用户名和密码
2.验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中
3.服务器向用户返回session_id,session信息都会写入到用户的Cookie
4.用户的每个后续请求都将通过在Cookie中取出session_id传给服务器
5.服务器收到session_id并对比之前保存的数据,确认用户的身份

1.导入依赖

JJWT是为了更友好在JVM上使用JWT,是基于JWT、JWS、JWE、JWK框架的java实现

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2.生成JWT(token)

// 需要一个签名密钥,因此创建一个签名密钥。通常密钥将从应用程序配置中读取
Key key = MacProvider.generateKey();

String compactJws = Jwts.builder()
        .setSubject("Joe")
        .signWith(SignatureAlgorithm.HS512, key)
        .compact();

在上边的例子中下,构建了一个JWT,该JWT将将注册的claim的sub(subject)设置为Joe,使用sha - 512算法在HMAC上注册JWT。最后,将它转换成字符串形式。

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJKb2UifQ.yiV1GWDrQyCeoOswYTf_xvlgsnaVVYJM0mU6rkmRBf2T1MBl3Xh2kZii0Q9BdX5-G0j25Qv2WF4lA6jPl5GKuA

验证一下jwt:

assert Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws).getBody().getSubject().equals("Joe");

这里有两个时间。之前的密钥被用来验证JWT的签名。如果未能验证JWT,则抛出一个签名异常。假设JWT已被验证,将解析claim并断言该sub被设置为Joe。

如果签名验证失败了怎么办?可以捕获签名异常并做出相应的反应:

try {
    // 可以相信这个JWT
    Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws);
} catch (SignatureException e) {
    // 不要相信JWT
}

案例:

public class JwtDemo {
    //加密算法
    private final static SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
    //私钥 / 生成签名的时候使用的秘钥secret,一般可以从本地配置文件中读取,切记这个秘钥不能外露,只在服务端使用,在任何场景都不应该流露出去。
    // 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
    private final static String secret = "secretKey";
    // 过期时间(单位秒)/ 2小时
    private final static Long access_token_expiration = 7200L;
    //jwt签发者
    private final static String jwt_iss = "spzhang";
    //jwt所有人
    private final static String subject = "zhangsp";

    /**
     * 创建jwt
     * @return
     *      返回生成的jwt token
     */
    public static String generateJwtToken(){
        // 头部 map / Jwt的头部承载,第一部分
        // 可不设置 默认格式是{"alg":"HS256"}
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        //载荷 map / Jwt的载荷,第二部分
        Map<String,Object> claims = new HashMap<String,Object>();
        //私有声明 / 自定义数据,根据业务需要添加
        claims.put("id","123456");
        claims.put("userName", "admin");

        //标准中注册的声明 (建议但不强制使用)
        //一旦写标准声明赋值之后,就会覆盖了那些标准的声明
        claims.put("iss", jwt_iss);
            /* iss: jwt签发者
                sub: jwt所面向的用户
                aud: 接收jwt的一方
                exp: jwt的过期时间,这个过期时间必须要大于签发时间
                nbf: 定义在什么时间之前,该jwt都是不可用的.
                iat: jwt的签发时间
                jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
            */
        //下面就是在为payload添加各种标准声明和私有声明了
        return Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
                .setHeader(map)         // 头部信息
                .setClaims(claims)      // 载荷信息
                .setId(UUID.randomUUID().toString()) // 设置jti(JWT ID):是JWT的唯一标识,从而回避重放攻击。
                .setIssuedAt(new Date())       // 设置iat: jwt的签发时间
                .setExpiration(new Date(System.currentTimeMillis() + access_token_expiration * 1000)) // 设置exp:jwt过期时间
                .setSubject(subject)    //设置sub:代表这个jwt所面向的用户,所有人
                .signWith(SIGNATURE_ALGORITHM, secret)//设置签名:通过签名算法和秘钥生成签名
                .compact(); // 开始压缩为xxxxx.yyyyy.zzzzz 格式的jwt token
    }

    /**
     *  从jwt中获取 载荷 信息
     * @param jwt
     * @return
     */
    private static Claims getClaimsFromJwt(String jwt) {
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

    public static void main(String[] args) {
        String jwtToken = generateJwtToken();
        System.out.println("JWT Token "+ jwtToken);
        System.out.println("=======================================================");

        Claims claims = getClaimsFromJwt(jwtToken);
        System.out.println(claims);
        System.out.println(claims.get("userName"));
    }

}

增强规范

body压缩。如果JWT体大,可以使用压缩解码器来压缩它。最重要的是,JJWT库将自动解压并解析JWT,而不需要额外的编码

String compactJws =  Jwts.builder()
        .setSubject("Joe")
        .compressWith(CompressionCodecs.DEFLATE)
        .signWith(SignatureAlgorithm.HS512, key)
        .compact();

如果检查Jws的header部分,它就会对这个进行解码:

{
  "alg": "HS512",
  "zip": "DEF"
}

JJWT自动检测到压缩是通过检查头来使用的,并且在解析时将自动解压。对于解压缩,不需要额外的编码。

要求claims。在解析时,您可以指定某些断言必须存在并设置为某个值。

try {
    Jws<Claims> claims = Jwts.parser()
            .requireSubject("Joe")
            .require("hasMotorcycle", true)
            .setSigningKey(key)
            .parseClaimsJws(compactJws);
} catch (MissingClaimException e) {
    // we get here if the required claim is not present
} catch (IncorrectClaimException e) {
    // we get here if the required claim has the wrong value
}

JWT Claims

JWT Claims Set表示一个JSON对象,它的成员是JWT传输的claims。JWT Claims Set中的Claim Names MUST是唯一的; JWT解析器MUST拒绝拥有重复member name的JWTs,或者使用JSON 解析只返回重复的成员name的最后一个词汇
一个JWT包含的claims必须是一组有效的,依赖上下文的。这方面的描述超出了本规范的范围。JWTs特定的applications将要求实现某种特定的方法能够明白和处理某些claims。然而,缺乏某些要求,所有的claims,在实现时,如果不明白它的一生,就MUST被忽略。

JWT Claims Names三种类型:
①Registered Claim Names
②Public Claim Names
③Private Claim Names

1.Registered Claim Names(约定注册)

下面的Claim Names是在IANA“JSON Web Token Claims”登记的约定俗称。就一切情况而论,下面定义的claims并不要求强制使用或实现。 而是在开始的时候提供一组使用的,有帮助的claims。 Applications使用JWTs应当定义他们使用哪些特定的claims,使用时是required还是optional。所有的names都是简短的,因为JWTs的核心目标是表达形式简洁紧凑

“iss” (Issuer) Claim

“iss” (issuer) claim定义了发布这个JWT的发行人。 如何处理这个claim是通常的应用程序指定。 “iss” 的值是大小写敏感的字符串,包含一个String Or URI值。使用这个claim是OPTIONAL

“sub”(Subject) Claim

“sub”(subject) claim定义了这个JWT的subject。JWT中的claims通常是关于subject的陈述。subject的值必须是:issuer上下文中的scpoed是本地唯一的,或者全局唯一的。如何处理这个claim通常是应用程序指定。“sub”的值是大小写敏感的字符串,包含一个StringOrURI值

“aud”(Audience) Claim

“aud”(audience) claim在JWT中用来标识收件人。每个打算处理JWT的当事人必须在audience claim中用值来识别自己。如果有这个claim,但处理这个claim的当事人无法用它的值识别自己,那么必须拒绝这个JWT。 在一般情况下,“aud”的值是有区分大小写的字符串构成的数组。每个字符串都包含一个StringOrURI值。在特殊情况下,当JWT只有一个audience,“aud”的值可能是一个大小写敏感的字符串,它包含一个StringOrURI值。audience的值的一般由应用程序指定

“exp”(Expiration Time) Claim

“exp”(expiration time) claim标识了失效时间,当达到或超过这个时间时。JWT MUST NOT接受和处理,处理“exp”claim要求当前的date/time必须在“exp”claim标识的date/time之前。考虑到时钟差,实现者MAY留有一点余地,通常不超过几分钟。它的值必须是一个number类型,包含一个NumericDate值

“nbf”(Not Before) Claim

“nbf”(not before) claim标识了时间点,当早于这个时间点,JWT MUST NOT被接受和处理。 处理这个“nbf”claim要求当前的date/time必须在 “nbf”claim所显示的date/time之后或者刚好在那个时间点。考虑到时钟差,实现者MAY留有一点余地,通常不超过几分钟。它的值 MUST 是一个number类型,包含一个NumericDate值

“iat”(Issued At) Claim

“iat”(issued at) claim标识了JWT所颁发的时间。这个claim能用于计算这个JWT已经使用的期限。它的值必须是一个number类型,包含一个NumericDate值

“jti”(JWT ID) Claim

“jti”(JWT ID) claim作为JWT的唯一标识符。这个标识符的值必须指定,在某种意思上确保相同的值在极小概率的情况下分配给另一个不同的数据对象。如果应用程序使用多个issuers,必须避免不同颁发者所产生的值可能一样的冲突。“jti”claim可以用于避免JWT被重复发送。“jti”的值是大小写敏感的


2.Public Claim Names

Claim Names可以被使用JWTs的人定义,然而,为了避免冲突,任何新的Claim Name应当满足:要么是IANA “JSON Web Token Claims” registry里面注册的值,要么是一个Public Name:一个能够防止命名冲突的Name。在各种情况下,定义name或者value的人需要警惕他们定义的Claim Name在namespace中能够得到控制。


3.Private Claim Names

JWT 的生产者和消费者可以约定他们所使用的Claim Names就是Private Names:这些names既不是Registered Claim Names,也不是Public Claim Names。 不像Public Claim Names,在使用Private Claim Names应当谨慎,以防止引起冲突


JJWT异常(JwtException基类)

JwtException是JWT相关运行时异常的基类,继承至RuntimeException。

(1)ClaimJwtException(abstract):验证失败

ClaimJwtException是JwtException的子类,它是在JTW声明验证失败后引发的

public abstract class ClaimJwtException extends JwtException {}

①ExpiredJwtException:证书过期
JWT证书在过期后引发的异常

public class ExpiredJwtException extends ClaimJwtException {}

②InvalidClaimException:声明(claim)不等于所需值时引发异常
当发现所需声明不等于所需值时引发异常,表明JWT无效且可能无法使用

// 无效claim
public class InvalidClaimException extends ClaimJwtException {}

// 不正确claim
public class IncorrectClaimException extends InvalidClaimException {}
// 不存在claim
public class MissingClaimException extends InvalidClaimException {}

③PrematureJwtException:被拒绝
异常表明JWT在被允许访问之前已被接受,并且必须被拒绝。

public class PrematureJwtException extends ClaimJwtException {}

(2)CompressionException:压缩或解压缩失败

表示压缩或解压缩JWT主体失败的异常

public class CompressionException extends JwtException {}

(3)MalformedJwtException:JWT构造不正确

异常表明JWT构造不正确,应予以拒绝

public class MalformedJwtException extends JwtException {}

(4)RequiredTypeException:参数类型不匹配

调用Claims#get(String,Class)且该值与Class参数的类型不匹配时引发异常。

public class RequiredTypeException extends JwtException {}

(5)SignatureException:计算、验证签名失败

异常表示计算签名或验证JWT的现有签名失败。

public class SignatureException extends JwtException {}

(6)UnsupportedJwtException:接受格式不匹配

接收到与应用程序预期格式不匹配的特定格式/配置的JWT时引发异常。
例如:当应用程序需要加密签名的声明JWS时,如果解析未签名的明文JWT,则会引发此异常。

public class UnsupportedJwtException extends JwtException {}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值