• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

jwtk/jjwt: Java JWT: JSON Web Token for Java and Android

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

jwtk/jjwt

开源软件地址:

https://github.com/jwtk/jjwt

开源编程语言:

Java 55.9%

开源软件介绍:

Build Status Coverage Status Gitter

Java JWT: JSON Web Token for Java and Android

JJWT aims to be the easiest to use and understand library for creating and verifying JSON Web Tokens (JWTs) on the JVM and Android.

JJWT is a pure Java implementation based exclusively on the JWT, JWS, JWE, JWK and JWA RFC specifications and open source under the terms of the Apache 2.0 License.

The library was created by Les Hazlewood and is supported and maintained by a community of contributors.

We've also added some convenience extensions that are not part of the specification, such as JWS compression and claim enforcement.

Table of Contents

Features

  • Fully functional on all JDKs and Android

  • Automatic security best practices and assertions

  • Easy to learn and read API

  • Convenient and readable fluent interfaces, great for IDE auto-completion to write code quickly

  • Fully RFC specification compliant on all implemented functionality, tested against RFC-specified test vectors

  • Stable implementation with enforced 100% test code coverage. Literally every single method, statement and conditional branch variant in the entire codebase is tested and required to pass on every build.

  • Creating, parsing and verifying digitally signed compact JWTs (aka JWSs) with all standard JWS algorithms:

    • HS256: HMAC using SHA-256
    • HS384: HMAC using SHA-384
    • HS512: HMAC using SHA-512
    • ES256: ECDSA using P-256 and SHA-256
    • ES384: ECDSA using P-384 and SHA-384
    • ES512: ECDSA using P-521 and SHA-512
    • RS256: RSASSA-PKCS-v1_5 using SHA-256
    • RS384: RSASSA-PKCS-v1_5 using SHA-384
    • RS512: RSASSA-PKCS-v1_5 using SHA-512
    • PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-2561
    • PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-3841
    • PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-5121

    1. Requires JDK 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.

  • Convenience enhancements beyond the specification such as

    • Body compression for any large JWT, not just JWEs
    • Claims assertions (requiring specific values)
    • Claim POJO marshaling and unmarshaling when using a compatible JSON parser (e.g. Jackson)
    • Secure Key generation based on desired JWA algorithms
    • and more...

Currently Unsupported Features

  • Non-compact serialization and parsing.
  • JWE (Encryption for JWT)

These features will be implemented in a future release. Community contributions are welcome!

Community

Getting Help

If you have trouble using JJWT, please first read the documentation on this page before asking questions. We try very hard to ensure JJWT's documentation is robust, categorized with a table of contents, and up to date for each release.

Questions

If the documentation or the API JavaDoc isn't sufficient, and you either have usability questions or are confused about something, please ask your question here.

After asking your question, you may wish to join our Slack or Gittr chat rooms, but note that they may not always be attended. You will usually have a better chance of having your question answered by asking your question here.

If you believe you have found a bug or would like to suggest a feature enhancement, please create a new GitHub issue, however:

Please do not create a GitHub issue to ask a question.

We use GitHub Issues to track actionable work that requires changes to JJWT's design and/or codebase. If you have a usability question, instead please ask your question here, or try Slack or Gittr as described above.

If a GitHub Issue is created that does not represent actionable work for JJWT's codebase, it will be promptly closed.

Bugs and Feature Requests

If you do not have a usability question and believe you have a legitimate bug or feature request, please do create a new JJWT issue.

If you feel like you'd like to help fix a bug or implement the new feature yourself, please read the Contributing section next before starting any work.

Contributing

Pull Requests

Simple Pull Requests that fix anything other than JJWT core code (documentation, JavaDoc, typos, test cases, etc) are always appreciated and have a high likelihood of being merged quickly. Please send them!

However, if you want or feel the need to change JJWT's functionality or core code, please do not issue a pull request without creating a new JJWT issue and discussing your desired changes first, before you start working on it.

It would be a shame to reject your earnest and genuinely appreciated pull request if it might not align with the project's goals, design expectations or planned functionality. We've sadly had to reject large PRs in the past because they were out of sync with project or design expectations - all because the PR author didn't first check in with the team first before working on a solution.

So, please create a new JJWT issue first to discuss, and then we can see if (or how) a PR is warranted. Thank you!

Help Wanted

If you would like to help, but don't know where to start, please visit the Help Wanted Issues page and pick any of the ones there, and we'll be happy to discuss and answer questions in the issue comments.

If any of those don't appeal to you, no worries! Any help you would like to offer would be appreciated based on the above caveats concerning contributing pull reqeuests. Feel free to discuss or ask questions first if you're not sure. :)

What is a JSON Web Token?

Don't know what a JSON Web Token is? Read on. Otherwise, jump on down to the Installation section.

JWT is a means of transmitting information between two parties in a compact, verifiable form.

The bits of information encoded in the body of a JWT are called claims. The expanded form of the JWT is in a JSON format, so each claim is a key in the JSON object.

JWTs can be cryptographically signed (making it a JWS) or encrypted (making it a JWE).

This adds a powerful layer of verifiability to the user of JWTs. The receiver has a high degree of confidence that the JWT has not been tampered with by verifying the signature, for instance.

The compact representation of a signed JWT is a string that has three parts, each separated by a .:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY

Each part is Base64URL-encoded. The first part is the header, which at a minimum needs to specify the algorithm used to sign the JWT. The second part is the body. This part has all the claims of this JWT encoded in it. The final part is the signature. It's computed by passing a combination of the header and body through the algorithm specified in the header.

If you pass the first two parts through a base 64 url decoder, you'll get the following (formatting added for clarity):

header

{
  "alg": "HS256"
}

body

{
  "sub": "Joe"
}

In this case, the information we have is that the HMAC using SHA-256 algorithm was used to sign the JWT. And, the body has a single claim, sub with value Joe.

There are a number of standard claims, called Registered Claims, in the specification and sub (for subject) is one of them.

To compute the signature, you need a secret key to sign it. We'll cover keys and algorithms later.

Installation

Use your favorite Maven-compatible build tool to pull the dependencies from Maven Central.

The dependencies could differ slightly if you are working with a JDK project or an Android project.

JDK Projects

If you're building a (non-Android) JDK project, you will want to define the following dependencies:

Maven

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<!-- Uncomment this next dependency if you are using JDK 10 or earlier and you also want to use 
     RSASSA-PSS (PS256, PS384, PS512) algorithms.  JDK 11 or later does not require it for those algorithms:
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
    <scope>runtime</scope>
</dependency>
-->

Gradle

dependencies {
    compile 'io.jsonwebtoken:jjwt-api:0.11.5'
    runtime 'io.jsonwebtoken:jjwt-impl:0.11.5',
    // Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
    //'org.bouncycastle:bcprov-jdk15on:1.70',
    'io.jsonwebtoken:jjwt-jackson:0.11.5' // or 'io.jsonwebtoken:jjwt-gson:0.11.5' for gson
}

Android Projects

Android projects will want to define the following dependencies and Proguard exclusions:

Dependencies

Add the dependencies to your project:

dependencies {
    api 'io.jsonwebtoken:jjwt-api:0.11.5'
    runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' 
    runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.5') {
        exclude group: 'org.json', module: 'json' //provided by Android natively
    }
    // Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
    //runtimeOnly 'org.bouncycastle:bcprov-jdk15on:1.70'
}

Proguard

You can use the following Android Proguard exclusion rules:

-keepattributes InnerClasses

-keep class io.jsonwebtoken.** { *; }
-keepnames class io.jsonwebtoken.* { *; }
-keepnames interface io.jsonwebtoken.* { *; }

-keep class org.bouncycastle.** { *; }
-keepnames class org.bouncycastle.** { *; }
-dontwarn org.bouncycastle.**

Understanding JJWT Dependencies

Notice the above dependency declarations all have only one compile-time dependency and the rest are declared as runtime dependencies.

This is because JJWT is designed so you only depend on the APIs that are explicitly designed for you to use in your applications and all other internal implementation details - that can change without warning - are relegated to runtime-only dependencies. This is an extremely important point if you want to ensure stable JJWT usage and upgrades over time:

JJWT guarantees semantic versioning compatibility for all of its artifacts except the jjwt-impl .jar. No such guarantee is made for the jjwt-impl .jar and internal changes in that .jar can happen at any time. Never add the jjwt-impl .jar to your project with compile scope - always declare it with runtime scope.

This is done to benefit you: great care goes into curating the jjwt-api .jar and ensuring it contains what you need and remains backwards compatible as much as is possible so you can depend on that safely with compile scope. The runtime jjwt-impl .jar strategy affords the JJWT developers the flexibility to change the internal packages and implementations whenever and however necessary. This helps us implement features, fix bugs, and ship new releases to you more quickly and efficiently.

Quickstart

Most complexity is hidden behind a convenient and readable builder-based fluent interface, great for relying on IDE auto-completion to write code quickly. Here's an example:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;

// We need a signing key, so we'll create one just for this example. Usually
// the key would be read from your application configuration instead.
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

String jws = Jwts.builder().setSubject("Joe").signWith(key).compact();

How easy was that!?

In this case, we are:

  1. building a JWT that will have the registered claim sub (subject) set to Joe. We are then
  2. signing the JWT using a key suitable for the HMAC-SHA-256 algorithm. Finally, we are
  3. compacting it into its final String form. A signed JWT is called a 'JWS'.

The resultant jws String looks like this:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60AlD4

Now let's verify the JWT (you should always discard JWTs that don't match an expected signature):

assert Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jws).getBody().getSubject().equals("Joe");

NOTE: Ensure you call the parseClaimsJws method (since there are many similar methods available). You will get an UnsupportedJwtException if you parse your JWT with wrong method.

There are two things going on here. The key from before is being used to validate the signature of the JWT. If it fails to verify the JWT, a SignatureException (which extends from JwtException) is thrown. Assuming the JWT is validated, we parse out the claims and assert that that subject is set to Joe.

You have to love code one-liners that pack a punch!

But what if parsing or signature validation failed? You can catch JwtException and react accordingly:

try {

    Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(compactJws);

    //OK, we can trust this JWT

} catch (JwtException e) {

    //don't trust the JWT!
}

Signed JWTs

The JWT specification provides for the ability to cryptographically sign a JWT. Signing a JWT:

  1. guarantees the JWT was created by someone we know (it is authentic) as well as
  2. guarantees that no-one has manipulated or changed the JWT after it was created (its integrity is maintained).

These two properties - authenticity and integrity - assure us that a JWT contains information we can trust. If a JWT fails authenticity or integrity checks, we should always reject that JWT because we can't trust it.

So how is a JWT signed? Let's walk through it with some easy-to-read pseudocode:

  1. Assume we have a JWT with a JSON header and body (aka 'Claims') as follows:

    header

    {
      "alg": "HS256"
    }
    

    body

    {
      "sub": "Joe"
    }
    
  2. Remove all unnecessary whitespace in the JSON:

    String header = '{"alg":"HS256"}'
    String claims = '{"sub":"Joe"}'
  3. Get the UTF-8 bytes and Base64URL-encode each:

    String encodedHeader = base64URLEncode( header.getBytes("UTF-8") )
    String encodedClaims = base64URLEncode( claims.getBytes("UTF-8") )
  4. Concatenate the encoded header and claims with a period character between them:

    String concatenated = encodedHeader + '.' + encodedClaims
  5. Use a sufficiently-strong cryptographic secret or private key, along with a signing algorithm of your choice (we'll use HMAC-SHA-256 here), and sign the concatenated string:

    Key key = getMySecretKey()
    byte[] signature = hmacSha256( concatenated, key )
  6. Because signatures are always byte arrays, Base64URL-encode the signature and append a period character '.' and it to the concatenated string:

    String jws = concatenated + '.' + base64URLEncode( signature )

And there you have it, the final jws String looks like this:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60AlD4

This is called a 'JWS' - short for signed JWT.

Of course, no one would want to do this manually in code, and worse, if you get anything wrong, you could cause security problems or weaknesses. As a result, JJWT was created to handle all of this for you: JJWT completely automates both the creation of JWSs as well as the parsing and verification of JWSs for you.

But before we dig in to showing you how to create a JWS using JJWT, let's briefly discuss Signature Algorithms and Keys, specifically as they relate to the JWT specifications. Understanding them is critical to being able to create a JWS properly.

Signature Algorithms Keys

The JWT specification identifies 12 standard signature algorithms - 3 secret key algorithms and 9 asymmetric key algorithms - identified by the following names:

  • HS256: HMAC using SHA-256
  • HS384: HMAC using SHA-384
  • HS512: HMAC using SHA-512
  • ES256: ECDSA using P-256 and SHA-256
  • ES384: ECDSA using P-384 and SHA-384
  • ES512: ECDSA using P-521 and SHA-512
  • RS256: RSASSA-PKCS-v1_5 using SHA-256
  • RS384: RSASSA-PKCS-v1_5 using SHA-384
  • RS512: RSASSA-PKCS-v1_5 using SHA-512
  • PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
  • PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
  • PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512

These are all represented in the io.jsonwebtoken.SignatureAlgorithm enum.

What's really important about these algorithms - other than their security properties - is that the JWT specification RFC 7518, Sections 3.2 through 3.5 requires (mandates) that you MUST use keys that are sufficiently strong for a chosen algorithm.

This means that JJWT - a specification-compliant library - will also enforce that you use sufficiently strong keys for the algorithms you choose. If you provide a weak key for a given algorithm, JJWT will reject it and throw an exception.

This is not because we want to make your life difficult, we promise! The reason why the JWT specification, and consequently JJWT, mandates key lengths is that the security model of a particular algorithm can completely break down if you don't adhere to the mandatory key properties of the algorithm, effectively having no security at all. No one wants completely insecure JWTs, right? Neither would we.

So what are the requirements?

HMAC-SHA

JWT HMAC-SHA signature algorithms HS256, HS384, and HS512 require a secret key that is at least as many bits as the algorithm's signature (digest) length per RFC 7512 Section 3.2. This means:

  • HS256 is HMAC-SHA-256, and that produces digests that are 256 bits (32 bytes) long, so HS256 requires that you use a secret key that is at least 32 bytes long.

  • HS384 is HMAC-SHA-384, and that produces digests that are 384 bits (48 bytes) long, so HS384 requires that you use a secret key that is at least 48 bytes long.

  • HS512 is HMAC-SHA-512, and that produces digests that are 512 bits (64 bytes) long, so HS512 requires that you use a secret key that is at least 64 bytes long.

RSA

JWT RSA signature algorithms RS256, RS384, RS512, PS256, PS384 and PS512 all require a minimum key length (aka an RSA modulus bit length) of 2048 bits per RFC 7512 Sections 3.3 and 3.5. Anything smaller than this (such as 1024 bits) will be rejected with an InvalidKeyException.

That said, in keeping with best practices and increasing key lengths for security longevity, JJWT recommends that you use:

  • at least 2048 bit keys with RS256 and PS256
  • at least 3072 bit keys with RS384 and PS384
  • at least 4096 bit keys with RS512 and PS512

These are only JJWT suggestions and not requirements. JJWT only enforces JWT specification requirements and for any RSA key, the requirement is the RSA key (modulus) length in bits MUST be >= 2048 bits.

Elliptic Curve

JWT Elliptic Curve signature algorithms ES256, ES384, and ES512 all require a key length (aka an Elliptic Curve order bit length) equal to the algorithm signature's individual R and S components per RFC 7512 Section 3.4. This means:

  • ES256 requires that you use a private key that is exactly 256 bits (32 bytes) long.

  • ES384 requires that you use a private key that is exactly 384 bits (48 bytes) long.

  • ES512 requires that you use a private key that is exactly 521 bits (65 or 66 bytes) long (depending on format).

Creating Safe Keys

If you don't want to think about bit length requirements or just want to make your life easier, JJWT has provided the io.jsonwebtoken.security.Keys utility class that can generate sufficiently secure keys for any given JWT signature algorithm you might want to use.

Secret Keys

If you want to generate a sufficiently strong SecretKey for use with the JWT HMAC-SHA algorithms, use the Keys.secretKeyFor(SignatureAlgorithm) helper method:

SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); //or HS384 or HS512

Under the hood, JJWT uses the JCA provider's KeyGenerator to create a secure-random key with the correct minimum length for the given algorithm.

If you need to save this new SecretKey, you can Base64 (or Base64URL) encode it:

String secretString = Encoders.BASE64.encode(key.getEncoded());

Ensure you save the resulting secretString somewhere safe - Base64-encoding is not encryption, so it's still considered sensitive information. You can further encrypt it, etc, before saving to disk (for example).

Asymmetric Keys

If you want to generate sufficiently strong Elliptic Curve or RSA asymmetric key pairs for use with JWT ECDSA or RSA algorithms, use the Keys.keyPairFor(SignatureAlgorithm) helper method:

KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256); //or RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512

You use the private key (keyPair.getPrivate()) to create a JWS and the public key (keyPair.getPublic()) to parse/verify a JWS.

NOTE: The PS256, PS384, and PS512 algorithms require JDK 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath. If you are using JDK 10 or earlier and you want to use them, see the Installation sec


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap