Бібліотека JWT (JSON Web Token) для Java [закрито]


77

Я працюю над веб-додатком, розробленим за допомогою Java та AngularJS, і вирішив реалізувати аутентифікацію та авторизацію маркера. З метою вправи я дійшов до того моменту, коли я надсилаю облікові дані на сервер, генерую випадковий маркер, що зберігає його, і відправляю назад клієнту. На кожен запит до сервера я вкладаю маркер у шапку, і він працює ідеально. Для точки зору автентифікації ідеальна і не потребує більше.

Однак зараз я хочу відстежувати тип користувача (адміністратор, звичайний користувач ...), а також його ідентифікатор або будь-яке інше унікальне поле; як я зрозумів, я повинен зашифрувати це в токені, який я надсилаю назад клієнту під час дії журналу. Це правильно?

Чи існує якась бібліотека JWT, яку ви використовували, і яка може генерувати, шифрувати та дешифрувати такі маркери? Буде дуже вдячне посилання на API бібліотеки та залежність Maven.

Дякую


1
Якщо інформація, яку ви хочете зберегти в маркері, не є конфіденційною, вам не обов'язково потрібно шифрувати маркер. Ідентифікатор користувача та дозволи є чимось секретним? Можливо, ні. Вам потрібно переконатися, що лише ви можете створити дійсний маркер. Підхід jwt полягає у тому, щоб маркер мав цифровий підпис Hmac та секретний ключ підпису, щоб ви змогли перевірити його цілісність та походження. Моя відповідь нижче містить бібліотеку та приклад.
Маркес,

1
Привіт .. Я теж намагаюся реалізувати цю бібліотеку JWT, і я це робив на стороні сервера (Java), але як я можу декодувати на своїй стороні (javascript)? Яку бібліотеку ви використовували для декодування її в частині angularjs?
Тьяго Міранда де Олівейра

Тіаго, я ні. Потік був таким: Вхід користувача -> дані, надіслані на сервер -> створений маркер -> відправлений назад клієнту. Щоразу, коли запит робиться на сервер, маркер додається в заголовок (я застосував для цього перехоплювач). Перевірка була проведена на сервері, і правильна відповідь була надіслана назад (якщо вона була дозволена чи ні).
Маріус Манастіреану

2
Корисна сторінка для спілкування з JWT: jwt.io
Баранаускас

@MariusManastireanu Токен, який ви надсилаєте з angular - той самий маркер, який ви отримали від сервера ?? Я працюю над тією ж частиною ... будь ласка, допоможіть
kittu

Відповіді:


53

JJWT прагне бути найпростішою у використанні та розумінні бібліотеки JWT для JVM та Android:

https://github.com/jwtk/jjwt


4
Простий, легкий і чистий, і працював відразу. Я спершу пішов на Google JsonToken і перейшов сюди після того, як возився з невирішеними залежностями та сторінками коду, щоб зібрати простий JWT.
Олівер Хауслер

1
Як я можу запобігти крадіжці токенів?
JHS

Як я можу обробити час очікування токена, як у nodejs-jsonwebtoken
vanduc1102

Я можу використовувати jjwt у простій тестовій програмі. Але коли я намагаюся використовувати його в моїй програмі dropwizard, я отримую таку помилку під час запуску: java.lang.NoSuchFieldError: WRITE_DURATIONS_AS_TIMESTAMPS. Думки?
Я.

1
"Як я можу обробити час очікування токена" - За специфікацією jwt вам потрібно встановити претензію "exp". Jjwt пропонує для цього зручний метод "setExpiration".
Я.

26

Якщо хтось потребує відповіді,

Я використовував цю бібліотеку: http://connect2id.com/products/nimbus-jose-jwt Maven тут: http://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt/2.10.1


2
Будь ласка, переконайтесь, що ви використовуєте останню версію. Бібліотека постійно переглядається та вдосконалюється, а також додаються нові алгоритми / функції: mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt Починаючи з версії 3.2, всі стандартні алгоритми підпису JWS повністю підтримуються, а також все шифрування JWE алгоритми, за винятком PBES2 та ECDH-ES.
Володимир Джувінов

Я отримую таку помилку: java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.decodeBase64
Ведмідь

Вищевказана проблема задокументована тут bitbucket.org/connect2id/nimbus-jose-jwt/issue/52/… . Я вирішив проблему, змінивши байт [] decode () methd у com.nimbusds.jose.util.Base64Url, щоб використовувати метод android.util.Base64.decode. Це правильний спосіб?
Ведмідь

1
У версії 2.24 ми перейшли з кодеку Apache Commons на внутрішню утиліту для обробки кодування + декодування BASE64. Завдяки цьому проблема Android з кодеком була успішно вирішена. Докладніше див. У виданні bitbucket.org/connect2id/nimbus-jose-jwt/issue/63 . Плюс ви повинні отримати ще кращі показники зараз :)
Володимир Джувінов

@MariusManastireanu Привіт, я починаю з аутентифікації angularjs + java за допомогою JWT. Не могли б ви, будь ласка, направити мене або показати зразок коду, як цього досягти?
kittu

19

Посилаючись на https://jwt.io/, ви можете знайти jwtреалізації багатьма мовами, включаючи java. Також сайт надає деяке порівняння між цим впровадженням (алгоритмами, які вони підтримують, та ....).

Для javaних згадані бібліотеки:


Дякую, це допомогло мені знайти плагін JWT для мого додатку play framework
chabeee

1
як ви, хлопці, обираєте, який із них використовувати? Що робити, якщо всі 4 бібліотеки охоплюють усі мої випадки використання?
jkerak

6
@jkerak Ідіть із тим, що вам здається найпростішим. Якщо всі реалізації здаються вам грецькими, перейдіть до тієї, що має найкрутішу назву. Це добре в мене вийшло.
TheFunk

@jkerak Я спробував перші дві бібліотеки. Jjwt lib дуже заплутаний, і мені знадобилося пару годин, щоб протестувати, хоча все ще не можу змусити його працювати. Github.com/auth0/java-jwt є найпростішим і просто працювали безпосередньо , без будь - яких зусиль. Високо рекомендую github.com/auth0/java-jwt
flame3

13

Здається, ця бібліотека працює добре: https://code.google.com/p/jsontoken/ .

Це залежить від Google Guava. Ось артефакти 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>

Бібліотека фактично використовується Google Wallet.

Ось як створити jwt, а також перевірити його та десериалізувати:

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;
    }
}

Це базується на коді тут: https://developers.google.com/wallet/instant-buy/about-jwts А тут: https://code.google.com/p/wallet-online-sample-java/source /browse/src/com/google/wallet/online/jwt/util/WalletOnlineService.java?r=08b3333bd7260b20846d7d96d3cf15be8a128dfa


5
Ця бібліотека використовує 1000 залежностей, які всі повинні бути вирішені, скомпільовані та скласти великий хаос у вашому проекті. Чому б переважно не використовувати те, що доступно в Java?
Олівер Хауслер

Не 1000s, це менше 20s залежностей, і працюйте для мене.
vanduc1102

ця бібліотека все ще активна ??
arvindwill

Не вдалося завантажити проект. Отримання 401
Валь Мартінес

8

IETF запропонував jose libs на своїй вікі: http://trac.tools.ietf.org/wg/jose/trac/wiki

Я настійно рекомендую використовувати їх для підписання. Я не хлопець з Java, але, схоже, jose4j здається хорошим варіантом. Також має хороші приклади: https://bitbucket.org/b_c/jose4j/wiki/JWS%20Examples

Оновлення: jwt.io забезпечує чітке порівняння кількох пов’язаних бібліотек jwt та їх особливостей. Обов’язкова перевірка!

Я хотів би почути, що віддають перевагу інші розробники Java.


jose4j також має багато підтримки JWT та хороші приклади її використання на додаток до прикладів JWS, на які ви вказали.
Брайан Кемпбелл



3

Якщо вам потрібно проаналізувати лише непідписані незашифровані маркери, ви можете використати цей код:

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

Це вилка оригінального google jsontoken

Він не оновлювався з 11 вересня 2012 року і залежить від деяких старих пакунків.

Що я зробив:

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.

Усі існуючі модульні тести пройшли разом із деякими нещодавно доданими тестовими кейсами.

Ось зразок для генерації маркера та перевірки маркера. Для отримання додаткової інформації, будь ласка, перевірте https://github.com/networknt/light вихідний код для використання.

Я є автором jsontoken та Omni-Channel Application Framework.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.