java-jwt ReadMe 文件中文翻译 ( R3.8.1 )

说明:本翻译仅仅作为个人笔记使用(因为每次看英文文档,看了第一遍,下次再去看又得慢慢翻译一遍,很反感,所以本人决定翻译的同时记录下来),本人水平有限,翻译的不好大家勿喷。当然这篇文章要能解决你的一些问题,那自然是最好的。

Java JWT

Jwt 的Java实现 JSON Web Token (JWT) - RFC 7519.
如果你想要了解Android版本的JWT,可以访问我们的另一个网址JWTDecode.Android .

安装

Maven

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

Gradle

implementation 'com.auth0:java-jwt:3.8.1'

可用的算法

本库实现JWT的验证与签名使用以下算法:

JWS Algorithm Description
HS256 HMAC256 HMAC with SHA-256
HS384 HMAC384 HMAC with SHA-384
HS512 HMAC512 HMAC with SHA-512
RS256 RSA256 RSASSA-PKCS1-v1_5 with SHA-256
RS384 RSA384 RSASSA-PKCS1-v1_5 with SHA-384
RS512 RSA512 RSASSA-PKCS1-v1_5 with SHA-512
ES256 ECDSA256 ECDSA with curve P-256 and SHA-256
ES384 ECDSA384 ECDSA with curve P-384 and SHA-384
ES512 ECDSA512 ECDSA with curve P-521 and SHA-512

使用

算法的使用

算法决定了token如何进行签名与验证。

在使用RSA或者ECDSA算法的情况下,可以用密码(未经过处理)的对其进行实例化:

//HMAC
Algorithm algorithmHS = Algorithm.HMAC256("secret");

在使用HMAC算法的情况下,可以用公私秘钥对或者KeyProvider的对其进行实例化:

//HMAC
//RSA
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);

当选用RSA或者ECDSA算法时,当你仅仅只需要进行签名,你可以使用null值来避免指定公钥。反过来,当你仅仅需要进行验证时,你可以用相同方式来避免指定私钥。例如:

Algorithm algorithmRS1 = Algorithm.RSA256(null, privateKey);//该算法仅仅用于签名的情况下
Algorithm algorithmRS2 = Algorithm.RSA256(publicKey, null);//该算法仅仅用于验证的情况下

使用KeyProvider:

通过使用KeyProvider你可以在运行状态下动态改变秘钥(RSA或者ECDSA算法下用于Token签名验证的秘钥或者用于对一个新的Token进行签名的秘钥)。这是通过实现RSAKeyProvider 或者ECDSAKeyProvider 方法来完成的。

  • getPublicKeyById(String kid):在Token签名验证期间对其进行调用,该方法的返回值是一个用于验证Token签名的公钥。 如果使用了 key rotation(Key转置), 例如. JWK 它能够通过id获得正确的转置秘钥(或者每次都只返回相同秘钥)。
  • getPrivateKey(): 在Token签名期间对其进行调用,该方法返回用于对JWT进行签名的秘钥。
  • getPrivateKeyId(): 在Token签名期间对其进行调用,该方法返回秘钥的Id,该Id用于标识getPrivateKey()返回的值。此值优于JWTCreator.Builder #withKeyId(String)方法中的值。如果您不需要设置kid值,请避免使用KeyProvider实例化算法。
    下边的例子将展示JwtStore是如何进行进行工作的(JwtStore是JWK Set的抽象实现)。要使用JWKS进行简单的Key转置,请尝试使用jwks-rsa-java库。
final JwkStore jwkStore = new JwkStore("{JWKS_FILE_HOST}");
final RSAPrivateKey privateKey = //Get the key instance
final String privateKeyId = //Create an Id for the above key

RSAKeyProvider keyProvider = new RSAKeyProvider() {
    @Override
    public RSAPublicKey getPublicKeyById(String kid) {
        //Received 'kid' value might be null if it wasn't defined in the Token's header
        RSAPublicKey publicKey = jwkStore.get(kid);
        return (RSAPublicKey) publicKey;
    }

    @Override
    public RSAPrivateKey getPrivateKey() {
        return privateKey;
    }

    @Override
    public String getPrivateKeyId() {
        return privateKeyId;
    }
};

Algorithm algorithm = Algorithm.RSA256(keyProvider);
//Use the Algorithm to create and verify JWTs.

创建Token并对其进行签名

首先你需要使用JWT.create()来创建一个JWTCreator 实例。其次使用其自带的builder来自定义你的Token所需要的声明。最后通过调用sign() 获取Token字符串并且传递Algorithm 实例。

  • 以下例子使用HS256
try {
    Algorithm algorithm = Algorithm.HMAC256("secret");
    String token = JWT.create()
        .withIssuer("auth0")
        .sign(algorithm);
} catch (JWTCreationException exception){
    //Invalid Signing configuration / Couldn't convert Claims.
}

*以下例子使用RS256

RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
try {
    Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
    String token = JWT.create()
        .withIssuer("auth0")
        .sign(algorithm);
} catch (JWTCreationException exception){
    //Invalid Signing configuration / Couldn't convert Claims.
}

如果无法将Claim转换为JSON或签名过程中使用的密钥无效,则会引发JWTCreationException类型的异常。

验证Token

首先你需要调用JWT.require()来创建一个JWTVerifier 实例,并且传递0Algorithm实例。如果你想将一些指定的Claim囊括在Token 中,可以使用builder来定义这些Calim。通过build()方法获得的实例是可以重复使用的,因此你只需要定义它一次,便可以用其来验证不同的Token.最后调用verify()传递Token.

  • 例子( 使用HS256
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
try {
    Algorithm algorithm = Algorithm.HMAC256("secret");
    JWTVerifier verifier = JWT.require(algorithm)
        .withIssuer("auth0")
        .build(); //Reusable verifier instance
    DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception){
    //Invalid signature/claims
}

*例子(使用RS256

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
try {
    Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
    JWTVerifier verifier = JWT.require(algorithm)
        .withIssuer("auth0")
        .build(); //Reusable verifier instance
    DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception){
    //Invalid signature/claims
}

如果令牌具有无效签名或未满足Claim要求,则会引发JWTVerificationException

Time Validation

JWT token中可能包含DateNumber 字段,该字段主要用来验证:

  • 该Token是在过去发行的,即"iat" < TODAY
  • 该Token目前为止没有过期,即"exp" > TODAY
  • 该Token已经可以使用了,即"nbf" < TODAY

在验证Token时,时间验证会自动执行验证,当Token的值被验证为无效时会抛出JWTVerificationException类型的异常信息。如果缺少任何先前的字段,则不会进行时间验证。

给应该仍然被视为有效的Token指定一个 leeway window说心理话,原谅我水平真的不够,我在google找了很多关于 leeway window的含义,却没能找到合理的解答,我也不知道其指的是什么意思。如果有哪位大神知道,欢迎留言帮忙,现在我只能硬生生的进行翻译),使用JWTVerifier builder的方法acceptLeeway() ,并传递一个正数值(表示秒数).

JWTVerifier verifier = JWT.require(algorithm)
    .acceptLeeway(1) // 1 sec for nbf, iat and exp
    .build();

您还可以为给定的Date声明指定自定义值,并仅覆盖该声明的默认值。

JWTVerifier verifier = JWT.require(algorithm)
    .acceptLeeway(1)   //1 sec for nbf and iat
    .acceptExpiresAt(5)   //5 secs for exp
    .build();

如果您需要在lib / app中测试这个特性,你可以通过将Verification实例强制转换为Verification来获得能接受自定义Clock的verification.build()方法。例如。:

BaseVerification verification = (BaseVerification) JWT.require(algorithm)
    .acceptLeeway(1)
    .acceptExpiresAt(5);
Clock clock = new CustomClock(); //Must implement Clock interface
JWTVerifier verifier = verification.build(clock);

Decode a Token

Token解码

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
try {
    DecodedJWT jwt = JWT.decode(token);
} catch (JWTDecodeException exception){
    //Invalid token
}

如果Token中带有无效语法或者标头或有效负载不是JSON,则会引发JWTDecodeException`类型的异常。

Header Claims(Header 声明)

Algorithm(算法) ("alg")

返回算法的值(如果Header中没有定义算法(alg)则返回null

String algorithm = jwt.getAlgorithm();

Type(类型) ("typ")

返回类型的值(如果Header中没有定义类型(typ)则返回null

String type = jwt.getType();

Content Type(内容类型) ("cty")

返回内容类型(Content Type)的值(如果Header中没有定义cty则返回null

String contentType = jwt.getContentType();

Key Id(秘钥ID) ("kid")

返回秘钥ID(Key Id)的值(如果Header中没有定义秘钥Id(kid)则返回null

String keyId = jwt.getKeyId();

Private Claims(私有声明)

如果你想获得其他在Token头部中定义的声明,可以调用getHeaderClaim() 方法,并以声明的名称作为参数。无论什么情况下,该方法都会返回一个Claim(即使没有找到该名称的Claim),并且你可以通过调用claim.isNull()来验证返回的Claim的值是否为空。

Claim claim = jwt.getHeaderClaim("owner");

使用JWT.create()创建Token时,您可以通过调用withHeader()并传递两个Claim Map来指定标题声明。

Map<String, Object> headerClaims = new HashMap();
headerClaims.put("owner", "auth0");
String token = JWT.create()
        .withHeader(headerClaims)
        .sign(algorithm);

签名完成后,algtyp值将始终包含在Header中。

Payload Claims(Payload 声明)

Issuer(发布者) ("iss")

返回发布者(Issuer)的值(如果Payload中没有定义发布者(iss)则返回null

String issuer = jwt.getIssuer();

Subject(主题) ("sub")

返回主题(sub)的值(如果Payload中没有定义主题(sub)则返回null

String subject = jwt.getSubject();

Audience(受众,即为谁发布的) ("aud")

返回Audience(aud)的值(如果Payload中没有定义Audience(aud)则返回null

List<String> audience = jwt.getAudience();

Expiration Time(过期时间) ("exp")

返回过期时间(exp)的值(如果Payload中没有定义过期时间(exp)则返回null

Date expiresAt = jwt.getExpiresAt();

Not Before (生效时间)("nbf")

返回生效时间(nbf)的值(如果Payload中没有定义生效时间(nbf)则返回null

Date notBefore = jwt.getNotBefore();

Issued At (签发时间)("iat")

返回签发时间(iat)的值(如果Payload中没有定义签发时间(iat)则返回null

Date issuedAt = jwt.getIssuedAt();

JWT ID(编号) ("jti")

返回编号(jti)的值(如果Payload中没有定义编号(jti)则返回null

String id = jwt.getId();

Private Claims(私有声明)

如果你想获得其他在Token Payload中定义的声明,可以调用getClaims() 或者getClaim()方法,并以声明的名称作为参数。无论什么情况下,该方法都会返回一个Claim(即使没有找到该名称的Claim),并且你可以通过调用claim.isNull()来验证返回的Claim的值是否为空。

Map<String, Claim> claims = jwt.getClaims();    //Key is the Claim name
Claim claim = claims.get("isAdmin");

or

Claim claim = jwt.getClaim("isAdmin");

在使用 JWT.create() 创建Token时,可以通过调用withClaim()来指定自定义声明,并通过传递namevalue两个参数来指定自定义Claim的名称和值。

String token = JWT.create()
        .withClaim("name", 123)
        .withArrayClaim("array", new Integer[]{1, 2, 3})
        .sign(algorithm);

你也可以在JWT.require()中调用withClaim()并传递name和必要的值作为参数来验证自定义的声明。

JWTVerifier verifier = JWT.require(algorithm)
    .withClaim("name", 123)
    .withArrayClaim("array", 1, 2, 3)
    .build();
DecodedJWT jwt = verifier.verify("my.jwt.token");

目前支持的自定义JWT声明创建和验证的类型有:
Boolean, Integer, Double, String, Date 和String and Integer两种类型的数组.

Claim Class(声明类)

Claim class is a wrapper for the Claim values. It allows you to get the Claim as different class types. The available helpers are:
Claim类是Claim值的包装器。它允许您将Claim作为不同的类类型。可用的助手是:

Primitives(原函数)

  • asBoolean(): Returns the Boolean value or null if it can't be converted.
  • asInt(): Returns the Integer value or null if it can't be converted.
  • asDouble(): Returns the Double value or null if it can't be converted.
  • asLong(): Returns the Long value or null if it can't be converted.
  • asString(): Returns the String value or null if it can't be converted.
  • asDate(): Returns the Date value or null if it can't be converted. This must be a NumericDate (Unix Epoch/Timestamp). Note that the JWT Standard specified that all the NumericDate values must be in seconds.
  • asBoolean():返回布尔值,如果无法转换,则返回null。
  • asInt():返回Integer值,如果无法转换,则返回null。
  • asDouble():返回Double值,如果无法转换,则返回null。
  • asLong():返回Long值,如果无法转换,则返回null。
  • asString():返回String值,如果无法转换,则返回null。
  • asDate():返回Date值,如果无法转换,则返回null。这必须是NumericDate(Unix Epoch / Timestamp)。请注意,JWT标准指定所有NumericDate类型的值必须以秒为单位。

自定义类和集合

要获得声明作为集合,您需要提供要转换的内容的类类型(Class Type)

  • as(class): 返回强制转换为Class Type的值。对于集合,您应该使用asArray和asList方法。
  • asMap(): 返回强制转换为Map <String,Object>的值。
  • asArray(class): 返回强制转换为Class Type类型的Array数组的值,如果该值不是JSON数组,则返回null。
  • asList(class): 返回强制转换为Class Type类型的List的值,如果值不是JSON Array,则返回null。

如果值无法转换为给定的类类型,则会引发JWTDecodeException类型的异常。

关于Auth0(以下我就不翻译了

Auth0可以 :

  • Add authentication with multiple authentication sources, either social like Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce, among others, or enterprise identity systems like Windows Azure AD, Google Apps, Active Directory, ADFS or any SAML Identity Provider.
  • Add authentication through more traditional username/password databases.
  • Add support for linking different user accounts with the same user.
  • Support for generating signed Json Web Tokens to call your APIs and flow the user identity securely.
  • Analytics of how, when and where users are logging in.
  • Pull data from other sources and add it to the user profile, through JavaScript rules.

Create a free account in Auth0

  1. Go to Auth0 and click Sign Up.
  2. Use Google, GitHub or Microsoft Account to login.

Issue Reporting

If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.

Author

Auth0

License

This project is licensed under the MIT license. See the LICENSE file for more info.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容