Thư viện JWT (JSON Web Token) cho Java [đã đóng]


77

Tôi đang làm việc trên một ứng dụng web được phát triển bằng Java và AngularJS và đã chọn triển khai xác thực và ủy quyền mã thông báo. Với mục đích tập luyện, tôi đã đến lúc tôi gửi thông tin đăng nhập đến máy chủ, tạo một kho lưu trữ mã thông báo ngẫu nhiên và gửi lại cho máy khách. Theo mọi yêu cầu tới máy chủ, tôi sẽ đính kèm mã thông báo trong tiêu đề và nó hoạt động hoàn hảo. Đối với quan điểm xác thực là hoàn hảo và sẽ không cần nhiều hơn nữa.

Tuy nhiên, bây giờ tôi muốn theo dõi loại người dùng (quản trị viên, người dùng thông thường ...), cũng như id của nó hoặc bất kỳ trường duy nhất nào khác; như tôi đã hiểu, tôi phải mã hóa mã đó trong mã thông báo mà tôi đang gửi lại cho khách hàng trong quá trình đăng nhập. Đúng không?

Có thư viện JWT nào mà bạn đã sử dụng và có thể tạo, mã hóa và giải mã các mã thông báo như vậy không? Một liên kết đến API của thư viện và sự phụ thuộc của Maven sẽ được đánh giá cao hơn nhiều.

Cảm ơn


1
Nếu thông tin bạn muốn lưu trữ trong mã thông báo không nhạy cảm, bạn không nhất thiết phải mã hóa mã thông báo. Id người dùng và quyền có phải là điều gì đó bí mật không? Có lẽ là không. Điều bạn cần đảm bảo là chỉ bạn mới có thể tạo mã thông báo hợp lệ. Cách tiếp cận jwt là có mã thông báo được ký điện tử bằng Hmac và khóa chữ ký bí mật để đảm bảo rằng bạn có thể xác minh tính toàn vẹn và nguồn gốc của nó. Câu trả lời của tôi dưới đây cung cấp một thư viện và ví dụ.
Marquez

1
Xin chào .. Tôi cũng đang cố gắng triển khai thư viện JWT này và tôi đã làm ở phía máy chủ (Java) nhưng làm cách nào để giải mã ở phía giao diện người dùng (javascript) của tôi? Bạn đã sử dụng thư viện nào để giải mã nó trên phần anglejs?
Thiago Miranda de Oliveira

Thiago, tôi không. Quy trình như sau: Người dùng đăng nhập -> dữ liệu được gửi đến máy chủ -> mã thông báo được tạo -> gửi lại cho máy khách. Bất cứ khi nào một yêu cầu được thực hiện đến máy chủ, mã thông báo được thêm vào tiêu đề (tôi đã triển khai một trình chặn cho việc này). Quá trình xác thực đã được thực hiện trên máy chủ và phản hồi chính xác đã được gửi lại (nếu nó được ủy quyền hay không).
Marius Manastireanu

2
Một trang hữu ích khi giao dịch với JWT: jwt.io
Vilmantas Baranauskas

@MariusManastireanu Mã thông báo bạn đang gửi từ angle có giống với mã thông báo bạn nhận được từ máy chủ không ?? Tôi đang làm phần tương tự ... xin vui lòng giúp đỡ
kittu

Câu trả lời:


53

JJWT nhằm trở thành thư viện JWT dễ sử dụng và dễ hiểu nhất cho JVM và Android:

https://github.com/jwtk/jjwt


4
Đơn giản, dễ dàng và sạch sẽ, và làm việc ngay lập tức. Tôi đã sử dụng Google JsonToken đầu tiên và chuyển sang đây sau khi lộn xộn với các trang và trang mã phụ thuộc chưa được giải quyết để lắp ráp một JWT đơn giản.
Oliver Hausler

1
Làm cách nào để ngăn chặn hành vi trộm cắp mã thông báo?
JHS

Làm thế nào tôi có thể xử lý Mã Timeout như trong nodejs-jsonwebtoken
vanduc1102

Tôi có thể sử dụng jjwt trong một chương trình thử nghiệm đơn giản. Nhưng khi tôi thử sử dụng nó trong ứng dụng dropwizard của mình, tôi gặp lỗi sau khi khởi động: java.lang.NoSuchFieldError: WRITE_DURATIONS_AS_TIMESTAMPS. Suy nghĩ?
Ừ.

1
"Làm cách nào để xử lý Token Timeout" - Theo thông số jwt, bạn cần đặt yêu cầu "exp". Jjwt cung cấp một phương thức "setExpiration" thuận tiện cho việc này.
Ừ.

26

Nếu ai cần câu trả lời,

Tôi đã sử dụng thư viện này: http://connect2id.com/products/nimbus-jose-jwt Maven tại đây: http://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt/2.10.1


2
Hãy đảm bảo rằng bạn sử dụng phiên bản mới nhất. Thư viện đang được liên tục xem xét & cải tiến và các thuật toán / tính năng mới đang được thêm vào: mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt Kể từ phiên bản 3.2, tất cả các thuật toán chữ ký JWS tiêu chuẩn cũng như được hỗ trợ đầy đủ cũng như tất cả mã hóa JWE algs, lưu cho PBES2 và ECDH-ES.
Vladimir Dzhuvinov

Tôi gặp phải lỗi sau: java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.decodeBase64
Chịu

Vấn đề trên được ghi lại ở đây bitbucket.org/connect2id/nimbus-jose-jwt/issue/52/… . Tôi đã giải quyết sự cố bằng cách sửa đổi byte [] decode () methd trong com.nimbusds.jose.util.Base64Url để sử dụng phương thức android.util.Base64.decode. Đây có phải là cách chính xác?
Chịu

1
Trong phiên bản 2.24, chúng tôi đã chuyển từ Apache Commons Codec sang một tiện ích nội bộ để xử lý mã hóa + giải mã BASE64. Cùng với đó, vấn đề Android với codec đã được giải quyết thành công. Xem sự cố bitbucket.org/connect2id/nimbus-jose-jwt/issue/63 để biết thêm chi tiết. Thêm vào đó bạn sẽ nhận được hiệu suất tốt hơn bây giờ :)
Vladimir Dzhuvinov

@MariusManastireanu Xin chào, tôi đang bắt đầu với xác thực anglejs + java bằng JWT. Bạn có thể vui lòng hướng dẫn tôi hoặc hiển thị một mã mẫu để làm thế nào để đạt được điều này?
kittu

19

Bằng cách tham khảo https://jwt.io/, bạn có thể tìm thấy các jwttriển khai bằng nhiều ngôn ngữ bao gồm java. Ngoài ra trang web cung cấp một số so sánh giữa các triển khai này (các thuật toán mà chúng hỗ trợ và ....).

Đối với javanhững thư viện này được đề cập:


Xin cảm ơn, nó đã giúp tôi để tìm JWT plugin cho khung chơi của tôi ứng dụng
chabeee

1
Làm thế nào để các bạn chọn cái nào để sử dụng? Điều gì sẽ xảy ra nếu cả 4 thư viện đều bao gồm tất cả các trường hợp sử dụng của tôi?
jkerak

6
@jkerak Bắt đầu với bất cứ điều gì mà bạn có vẻ đơn giản nhất để thực hiện. Nếu tất cả các triển khai dường như là hy sinh đối với bạn, hãy chọn cái có tên thú vị nhất. Điều này đã làm việc tốt cho tôi.
TheFunk

@jkerak Tôi đã thử hai thư viện đầu tiên. Jjwt lib rất khó hiểu và tôi đã mất vài giờ để kiểm tra xung quanh trong khi vẫn không thể làm cho nó hoạt động. Các github.com/auth0/java-jwt là đơn giản và chỉ cần làm việc trực tiếp mà không cần bất kỳ nỗ lực. Thực sự giới thiệu github.com/auth0/java-jwt
flame3

13

Thư viện này có vẻ hoạt động tốt: https://code.google.com/p/jsontoken/ .

Nó phụ thuộc vào Google Guava. Dưới đây là các hiện vật của Maven:

<dependency>
    <groupId>com.googlecode.jsontoken</groupId>
    <artifactId>jsontoken</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

Thư viện trên thực tế được sử dụng bởi Google Wallet.

Đây là cách tạo một jwt và để xác minh nó và giải mã hóa nó:

import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.Calendar;
import java.util.List;

import net.oauth.jsontoken.JsonToken;
import net.oauth.jsontoken.JsonTokenParser;
import net.oauth.jsontoken.crypto.HmacSHA256Signer;
import net.oauth.jsontoken.crypto.HmacSHA256Verifier;
import net.oauth.jsontoken.crypto.SignatureAlgorithm;
import net.oauth.jsontoken.crypto.Verifier;
import net.oauth.jsontoken.discovery.VerifierProvider;
import net.oauth.jsontoken.discovery.VerifierProviders;

import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;

import com.google.common.collect.Lists;
import com.google.gson.JsonObject;


/**
 * Provides static methods for creating and verifying access tokens and such. 
 * @author davidm
 *
 */
public class AuthHelper {

    private static final String AUDIENCE = "NotReallyImportant";

    private static final String ISSUER = "YourCompanyOrAppNameHere";

    private static final String SIGNING_KEY = "LongAndHardToGuessValueWithSpecialCharacters@^($%*$%";

    /**
     * Creates a json web token which is a digitally signed token that contains a payload (e.g. userId to identify 
     * the user). The signing key is secret. That ensures that the token is authentic and has not been modified.
     * Using a jwt eliminates the need to store authentication session information in a database.
     * @param userId
     * @param durationDays
     * @return
     */
    public static String createJsonWebToken(String userId, Long durationDays)    {
        //Current time and signing algorithm
        Calendar cal = Calendar.getInstance();
        HmacSHA256Signer signer;
        try {
            signer = new HmacSHA256Signer(ISSUER, null, SIGNING_KEY.getBytes());
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        }

        //Configure JSON token
        JsonToken token = new net.oauth.jsontoken.JsonToken(signer);
        token.setAudience(AUDIENCE);
        token.setIssuedAt(new org.joda.time.Instant(cal.getTimeInMillis()));
        token.setExpiration(new org.joda.time.Instant(cal.getTimeInMillis() + 1000L * 60L * 60L * 24L * durationDays));

        //Configure request object, which provides information of the item
        JsonObject request = new JsonObject();
        request.addProperty("userId", userId);

        JsonObject payload = token.getPayloadAsJsonObject();
        payload.add("info", request);

        try {
            return token.serializeAndSign();
        } catch (SignatureException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Verifies a json web token's validity and extracts the user id and other information from it. 
     * @param token
     * @return
     * @throws SignatureException
     * @throws InvalidKeyException
     */
    public static TokenInfo verifyToken(String token)  
    {
        try {
            final Verifier hmacVerifier = new HmacSHA256Verifier(SIGNING_KEY.getBytes());

            VerifierProvider hmacLocator = new VerifierProvider() {

                @Override
                public List<Verifier> findVerifier(String id, String key){
                    return Lists.newArrayList(hmacVerifier);
                }
            };
            VerifierProviders locators = new VerifierProviders();
            locators.setVerifierProvider(SignatureAlgorithm.HS256, hmacLocator);
            net.oauth.jsontoken.Checker checker = new net.oauth.jsontoken.Checker(){

                @Override
                public void check(JsonObject payload) throws SignatureException {
                    // don't throw - allow anything
                }

            };
            //Ignore Audience does not mean that the Signature is ignored
            JsonTokenParser parser = new JsonTokenParser(locators,
                    checker);
            JsonToken jt;
            try {
                jt = parser.verifyAndDeserialize(token);
            } catch (SignatureException e) {
                throw new RuntimeException(e);
            }
            JsonObject payload = jt.getPayloadAsJsonObject();
            TokenInfo t = new TokenInfo();
            String issuer = payload.getAsJsonPrimitive("iss").getAsString();
            String userIdString =  payload.getAsJsonObject("info").getAsJsonPrimitive("userId").getAsString();
            if (issuer.equals(ISSUER) && !StringUtils.isBlank(userIdString))
            {
                t.setUserId(new ObjectId(userIdString));
                t.setIssued(new DateTime(payload.getAsJsonPrimitive("iat").getAsLong()));
                t.setExpires(new DateTime(payload.getAsJsonPrimitive("exp").getAsLong()));
                return t;
            }
            else
            {
                return null;
            }
        } catch (InvalidKeyException e1) {
            throw new RuntimeException(e1);
        }
    }


}

public class TokenInfo {
    private ObjectId userId;
    private DateTime issued;
    private DateTime expires;
    public ObjectId getUserId() {
        return userId;
    }
    public void setUserId(ObjectId userId) {
        this.userId = userId;
    }
    public DateTime getIssued() {
        return issued;
    }
    public void setIssued(DateTime issued) {
        this.issued = issued;
    }
    public DateTime getExpires() {
        return expires;
    }
    public void setExpires(DateTime expires) {
        this.expires = expires;
    }
}

Điều này dựa trên mã tại đây: https://developers.google.com/wallet/instant-buy/about-jwts Và tại đây: https://code.google.com/p/wallet-online-sample-java/source /browse/src/com/google/wallet/online/jwt/util/WalletOnlineService.java?r=08b3333bd7260b20846d7d96d3cf15be8a128dfa


5
Thư viện này sử dụng hàng nghìn phụ thuộc, tất cả đều cần được giải quyết, biên dịch và tạo ra một mớ hỗn độn lớn cho dự án của bạn. Tại sao không chủ yếu sử dụng những gì có sẵn trong Java?
Oliver Hausler

Không phải 1000s, đó là ít hơn 20s phụ thuộc và phù hợp với tôi.
vanduc1102

lib này vẫn còn hoạt động ??
arvindwill

Không thể tải xuống dự án. Nhận 401
Val Martinez

8

IETF đã đề xuất jose libs trên wiki của nó: http://trac.tools.ietf.org/wg/jose/trac/wiki

Tôi thực sự khuyên bạn nên sử dụng chúng để ký. Tôi không phải là một chàng trai Java, nhưng có vẻ như jose4j có vẻ là một lựa chọn tốt. Có các ví dụ hay: https://bitbucket.org/b_c/jose4j/wiki/JWS%20Examples

Cập nhật: jwt.io cung cấp một so sánh ngắn gọn về một số thư viện liên quan đến jwt và các tính năng của chúng. A phải kiểm tra!

Tôi rất muốn nghe về những gì các nhà phát triển java khác thích.


jose4j cũng có nhiều hỗ trợ JWT và các ví dụ tốt về việc sử dụng nó ngoài các ví dụ JWS mà bạn đã chỉ ra.
Brian Campbell



3

Nếu bạn chỉ cần phân tích cú pháp mã thông báo chưa được mã hóa chưa ký, bạn có thể sử dụng mã này:

boolean parseJWT_2() {
    String authToken = getToken();
    String[] segments = authToken.split("\\.");
    String base64String = segments[1];
    int requiredLength = (int)(4 * Math.ceil(base64String.length() / 4.0));
    int nbrPaddings = requiredLength - base64String.length();

    if (nbrPaddings > 0) {
        base64String = base64String + "====".substring(0, nbrPaddings);
    }

    base64String = base64String.replace("-", "+");
    base64String = base64String.replace("_", "/");

    try {
        byte[] data = Base64.decode(base64String, Base64.DEFAULT);

        String text;
        text = new String(data, "UTF-8");
        tokenInfo = new Gson().fromJson(text, TokenInfo.class);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }

    return true;
}

2

https://github.com/networknt/jsontoken

Đây là một nhánh của google jsontoken gốc

Nó đã không được cập nhật kể từ ngày 11 tháng 9 năm 2012 và phụ thuộc vào một số gói cũ.

Những gì tôi đã làm xong:

Convert from Joda time to Java 8 time. So it requires Java 8.
Covert Json parser from Gson to Jackson as I don't want to include two Json parsers to my projects.
Remove google collections from dependency list as it is stopped long time ago.
Fix thread safe issue with Java Mac.doFinal call.

Tất cả các bài kiểm tra đơn vị hiện có đã vượt qua cùng với một số trường hợp kiểm thử mới được thêm vào.

Đây là một mẫu để tạo mã thông báo và xác minh mã thông báo. Để biết thêm thông tin, vui lòng kiểm tra mã nguồn https://github.com/networknt/light để sử dụng.

Tôi là tác giả của cả Khung ứng dụng jsontoken và Omni-Channel.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.