java编程之java jwt token使用,autho0的Java-jwt框架使用,java编程,java-jwt<h2>一、前言</h2>
Java编程中使用jwt,首先你必须了解jwt是什么,长什么样子。如果你不了解可以先去本站另外一篇博客<a href="http://www.leftso.com/blog/220.html" rel="" target="_blank" >什么是JWT?</a><br />
<h2>二、Java编程中jwt框架选择</h2>
在Java编程中,实现jwt标准的有很多框架,本博客采用的框架是auth0的java-jwt版本为3.2.0
<h2>三、什么是Java-JWT</h2>
auth0的java-jwt是一个JSON WEB TOKEN(JWT)的一个实现。
<h2>四、安装下载相关依赖</h2>
如果你是采用<strong>maven</strong>的方式,在你的项目pom.xml文件中添加以下java-jwt的依赖片段:
<pre>
<code class="language-xml"><dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency></code></pre>
如果你是采用<strong>Gradle</strong>的方式,则添加以下内容:
<pre>
<code>compile 'com.auth0:java-jwt:3.2.0'</code></pre>
<h2>五、java-jwt已经实现的算法</h2>
该库使用以下算法实现JWT验证和签名:
<table class="table table-bordered table-hover" >
<thead>
<tr>
<th>JWS</th>
<th>算法</th>
<th>介绍</th>
</tr>
</thead>
<tbody>
<tr>
<td>HS256</td>
<td>HMAC256</td>
<td>HMAC with SHA-256</td>
</tr>
<tr>
<td>HS384</td>
<td>HMAC384</td>
<td>HMAC with SHA-384</td>
</tr>
<tr>
<td>HS512</td>
<td>HMAC512</td>
<td>HMAC with SHA-512</td>
</tr>
<tr>
<td>RS256</td>
<td>RSA256</td>
<td>RSASSA-PKCS1-v1_5 with SHA-256</td>
</tr>
<tr>
<td>RS384</td>
<td>RSA384</td>
<td>RSASSA-PKCS1-v1_5 with SHA-384</td>
</tr>
<tr>
<td>RS512</td>
<td>RSA512</td>
<td>RSASSA-PKCS1-v1_5 with SHA-512</td>
</tr>
<tr>
<td>ES256</td>
<td>ECDSA256</td>
<td>ECDSA with curve P-256 and SHA-256</td>
</tr>
<tr>
<td>ES384</td>
<td>ECDSA384</td>
<td>ECDSA with curve P-384 and SHA-384</td>
</tr>
<tr>
<td>ES512</td>
<td>ECDSA512</td>
<td>ECDSA with curve P-521 and SHA-512</td>
</tr>
</tbody>
</table>
<h2>六、如何使用java-jwt</h2>
<strong>6.1.选择一种算法</strong>
<p> 算法定义了一个令牌是如何被签名和验证的。它可以用HMAC算法的原始值来实例化,也可以在RSA和ECDSA算法的情况下对密钥对或密钥提供程序进行实例化。创建后,该实例可用于令牌签名和验证操作。</p>
<p>在使用RSA或ECDSA算法时,只需要签署JWTs,就可以通过传递null值来避免指定公钥。当您需要验证JWTs时,也可以使用私钥进行操作<br />
<br />
使用静态的字符密文或者key来获取算法器:<br />
</p>
<pre>
<code class="language-java">//HMAC
Algorithm algorithmHS = Algorithm.HMAC256("secret");
//RSA
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);</code></pre>
使用一个key提供者来获取算法:<br />
通过使用KeyProvider,您可以在运行时更改密钥,用于验证令牌签名或为RSA或ECDSA算法签署一个新的令牌。这是通过实现RSAKeyProvider或ECDSAKeyProvider方法实现的:
<ul>
<li><code>getPublicKeyById(String kid)</code>: 它在令牌签名验证中调用,它应该返回用于验证令牌的密钥。如果使用了关键的轮换,例如JWK,它可以使用id来获取正确的轮换键(或者只是一直返回相同的键)。</li>
<li><code>getPrivateKey()</code>: 在令牌签名期间调用它,它应该返回用于签署JWT的密钥。</li>
<li><code>getPrivateKeyId()</code>:在令牌签名期间调用它,它应该返回标识由getPrivateKey()返回的键的id的id。这个值比JWTCreator.Builder和keyid(String)方法中的值更受欢迎。如果您不需要设置孩子的值,就避免使用KeyProvider实例化算法。</li>
</ul>
下面的代码片段将展示如何使用:
<pre>
<code class="language-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.</code></pre>
<br />
<em>提示:对于使用JWKs的简单的键轮换,可以尝试JWKs-rsa-java库。</em><br />
<br />
<strong>6.2.创建一个签名的JWT token</strong><br />
首先需要通过调用<code>jwt.create()</code>创建一个<code>JWTCreator</code>实例
<ul>
<li>例如使用 <code>HS256算法:</code></li>
</ul>
<pre>
<code class="language-java">try {
Algorithm algorithm = Algorithm.HMAC256("secret");
String token = JWT.create()
.withIssuer("auth0")
.sign(algorithm);
} catch (UnsupportedEncodingException exception){
//UTF-8 encoding not supported
} catch (JWTCreationException exception){
//Invalid Signing configuration / Couldn't convert Claims.
}</code></pre>
<ul>
<li>例如使用<code>RS256算法:</code></li>
</ul>
<pre>
<code class="language-java">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.
}</code></pre>
如果Claim不能转换为JSON,或者在签名过程中使用的密钥无效,那么将会抛出<code>JWTCreationException</code>异常。<br />
<br />
<strong>6.3.验证令牌</strong><br />
首先需要通过调用jwt.require()和传递算法实例来创建一个JWTVerifier实例。如果您要求令牌具有特定的Claim值,请使用构建器来定义它们。方法build()返回的实例是可重用的,因此您可以定义一次,并使用它来验证不同的标记。最后调用verifier.verify()来验证token
<ul>
<li>例如使用 <code>HS256算法的时候:</code></li>
</ul>
<pre>
<code class="language-java">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 (UnsupportedEncodingException exception){
//UTF-8 encoding not supported
} catch (JWTVerificationException exception){
//Invalid signature/claims
}</code></pre>
<ul>
<li>例如使用 <code>RS256算法的时候:</code></li>
</ul>
<pre>
<code class="language-java">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
}</code></pre>
如果令牌有一个无效的签名,或者没有满足Claim要求,那么将会抛出JWTVerificationException异常<br />
<br />
<strong>6.4.jwt时间的验证</strong><br />
JWT令牌可能包括可用于验证的DateNumber字段:
<ul>
<li>这个令牌发布了一个过期的时间 <code>"iat" < TODAY</code></li>
<li>这个令牌还没过期 <code>"exp" > TODAY</code> and</li>
<li>这个令牌已经被使用了. <code>"nbf" > TODAY</code></li>
</ul>
当验证一个令牌时,时间验证会自动发生,导致在值无效时抛出一个JWTVerificationException。如果前面的任何一个字段都丢失了,那么在这个验证中就不会考虑这些字段。<br />
要指定令牌仍然被认为有效的余地窗口,在JWTVerifier builder中使用accept回旋()方法,并传递一个正值的秒值。这适用于上面列出的每一项。
<pre>
<code class="language-java">JWTVerifier verifier = JWT.require(algorithm)
.acceptLeeway(1) // 1 sec for nbf, iat and exp
.build();</code></pre>
您还可以为给定的日期声明指定一个自定义值,并为该声明覆盖缺省值。
<pre>
<code class="language-java">JWTVerifier verifier = JWT.require(algorithm)
.acceptLeeway(1) //1 sec for nbf and iat
.acceptExpiresAt(5) //5 secs for exp
.build();</code></pre>
如果您需要在您的lib/app中测试此行为,将验证实例转换为basever可视化,以获得verific.build()方法的可见性,该方法可以接受定制的时钟。例如:
<pre>
<code class="language-java">BaseVerification verification = (BaseVerification) JWT.require(algorithm)
.acceptLeeway(1)
.acceptExpiresAt(5);
Clock clock = new CustomClock(); //Must implement Clock interface
JWTVerifier verifier = verification.build(clock);</code></pre>
<br />
<strong>6.5解码一个jwt令牌</strong>
<pre>
<code class="language-java">String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
try {
DecodedJWT jwt = JWT.decode(token);
} catch (JWTDecodeException exception){
//Invalid token
}</code></pre>
如果令牌有无效的语法,或者消息头或有效负载不是JSONs,那么将会抛出JWTDecodeException异常。<br />
<br />
<strong>6.6JWT头部解析</strong>
<h4><br />
Algorithm ("alg")</h4>
返回jwt的算法值或,如果没有定义则返回null
<pre>
<code class="language-java">String algorithm = jwt.getAlgorithm();</code></pre>
<p> </p>
<p>Type ("typ")</p>
<p>返回jwt的类型值,如果没有定义则返回null(多数情况类型值为jwt)</p>
<pre>
<code class="language-java">String type = jwt.getType();</code></pre>
<p> </p>
<p>Content Type ("cty")</p>
<p>返回内容的类型,如果没有定义则返回null</p>
<pre>
<code class="language-java">String contentType = jwt.getContentType();</code></pre>
<p><br />
Key Id ("kid")<br />
返回key的id值,如果没有定义则返回null<br />
</p>
<pre>
<code class="language-java">String keyId = jwt.getKeyId();</code></pre>
<h4>私有的Claims,即自定义字段</h4>
在令牌的头部中定义的附加声明可以通过调用getHeaderClaim() 获取,即使无法找到,也会返回。您可以通过调用claim.isNull()来检查声明的值是否为null。
<pre>
<code class="language-java">Claim claim = jwt.getHeaderClaim("owner");</code></pre>
当使用jwt.create()创建一个令牌时,您可以通过调用withHeader()来指定头声明,并同时传递声明的映射。
<pre>
<code class="language-java">Map<String, Object> headerClaims = new HashMap();
headerClaims.put("owner", "auth0");
String token = JWT.create()
.withHeader(headerClaims)
.sign(algorithm);
</code></pre>
<em>提示:在签名过程之后,alg和typ值将始终包含在Header中。</em><br />
<br />
<strong>6.7JWT的负载(Payload)声明</strong>
<h4>Issuer ("iss")</h4>
返回签发者的名称值,如果没有在负载中定义则返回null
<pre>
<code class="language-java">String issuer = jwt.getIssuer();</code></pre>
<h4>Subject ("sub")</h4>
返回jwt所面向的用户的值,如果没有在负载中定义则返回null
<pre>
<code class="language-java">String subject = jwt.getSubject();</code></pre>
<h4>Audience ("aud")</h4>
返回该jwt由谁接收,如果没有在负载中定义则返回null
<pre>
<code class="language-java">List<String> audience = jwt.getAudience();</code></pre>
<h4>Expiration Time ("exp")</h4>
返回该jwt的过期时间,如果在负载中没有定义则返回null
<pre>
<code class="language-java">Date expiresAt = jwt.getExpiresAt();</code></pre>
<h4>Not Before ("nbf")</h4>
Returns the Not Before value or null if it's not defined in the Payload.
<pre>
<code class="language-java">Date notBefore = jwt.getNotBefore();</code></pre>
<h4>Issued At ("iat")</h4>
返回在什么时候签发的,如果在负载中没有定义则返回null
<pre>
<code class="language-java">Date issuedAt = jwt.getIssuedAt();</code></pre>
<h4>JWT ID ("jti")</h4>
返回该jwt的唯一标志,如果在负载中没有定义则返回null
<pre>
<code class="language-java">String id = jwt.getId();</code></pre>
<strong>自定义声明</strong><br />
在令牌有效负载中定义的附加声明可以通过调用getClaims()或 getClaim()和传递声明名来获得。即使无法找到声明,也将会有返回值。您可以通过调用claim.isNull()来检查声明的值是否为null。
<pre>
<code class="language-java">Map<String, Claim> claims = jwt.getClaims(); //Key is the Claim name
Claim claim = claims.get("isAdmin");</code></pre>
或者:
<pre>
<code class="language-java">Claim claim = jwt.getClaim("isAdmin");</code></pre>
当使用jwt.create()创建一个令牌时,您可以通过调用withClaim()来指定自定义声明,并同时传递名称和值。
<pre>
<code class="language-java">String token = JWT.create()
.withClaim("name", 123)
.withArrayClaim("array", new Integer[]{1, 2, 3})
.sign(algorithm);</code></pre>
您还可以通过调用withClaim()来验证jwt.require()的自定义声明,并传递该名称和所需的值。
<pre>
<code class="language-java">JWTVerifier verifier = JWT.require(algorithm)
.withClaim("name", 123)
.withArrayClaim("array", 1, 2, 3)
.build();
DecodedJWT jwt = verifier.verify("my.jwt.token");</code></pre>
<em>提示:当前支持的自定义JWT声明创建和验证的类型是:Boolean, Integer, Double, String, Date 和Arrays。</em><br />
<br />
<strong>6.8Claim Class</strong><br />
索赔类是索赔值的包装器。它允许您将索赔作为不同的类类型。可用的工具方法:<br />
原始的:
<ul>
<li><strong>asBoolean()</strong>: 返回布尔值,如果不能转换返回null。</li>
<li><strong>asInt()</strong>: 返回整数值,如果不能转换返回null。</li>
<li><strong>asDouble()</strong>: 返回 Double 值,如果不能转换则返回null。</li>
<li><strong>asLong()</strong>: 返回Long 值,如果不能转换则返回null。</li>
<li><strong>asString()</strong>: 返回String值,如果不能转换则返回null。</li>
<li><strong>asDate()</strong>: 返回 Date值,如果不能转换则返回null。 必须是一个数字日期 (Unix 系统时间戳). 注意,JWT标准指定所有的数字日期值必须以秒为单位。</li>
</ul>
<h4>自定义类型和集合:</h4>
要获得作为集合的声明,您需要提供要转换的内容的类类型
<ul>
<li>as(class): 返回 Class Type 的解析值. 对于集合,您应该使用asArray和asList方法。</li>
<li>asMap(): 返回被转换为 Map<String, Object>的值</li>
<li>asArray(class): 返回被转换成元素类型的 Class Type, or null if the value isn't a JSON Array.</li>
<li>asList(class): 返回集合元素的 Class Type, or null if the value isn't a JSON Array.</li>
</ul>
<em>如果不能将值转换为给定的类类型,则会抛出JWTDecodeException异常</em><br />
<br />
<em>翻译的不标准的后续更近,欢迎提供宝贵意见或者翻译,联系leftso@qq.com</em>