【问题标题】:JWT (JSON Web Token) library for Java [closed]Java的JWT(JSON Web Token)库[关闭]
【发布时间】:2014-07-11 14:05:54
【问题描述】:

我正在开发一个使用 Java 和 AngularJS 开发的 Web 应用程序,并选择实现令牌身份验证和授权。 出于练习的目的,我已经将凭据发送到服务器,生成一个随机令牌存储它并将其发送回客户端。 在对服务器的每个请求中,我都将令牌附加到标头中,并且效果很好。 对于身份验证的观点是完美的,不需要更多。

但是,我现在想跟踪用户类型(管理员、普通用户...),以及它的 id 或任何其他唯一字段;据我了解,我必须在登录操作期间发送回客户端的令牌中对其进行加密。对吗?

您是否使用过任何 JWT 库并且可以生成、加密和解密此类令牌? 非常感谢您提供指向该库的 API 和 Maven 依赖项的链接。

谢谢

【问题讨论】:

  • 如果要存储在令牌中的信息不敏感,则不一定需要加密令牌。用户 ID 和权限是秘密的吗?可能没有。您需要确保只有您可以创建有效的令牌。 jwt 方法是使用 Hmac 和秘密签名密钥对令牌进行数字签名,以确保您能够验证其完整性和来源。我在下面的回答提供了一个库和示例。
  • 嗨..我也在尝试实现这个 JWT 库,我在服务器端 (Java) 做了,但是如何在我的前端 (javascript) 解码?你用哪个库在 angularjs 部分解码它?
  • 蒂亚戈,我没有。流程如下:用户登录 -> 数据发送到服务器 -> 令牌创建 -> 发送回客户端。每当向服务器发出请求时,令牌都会附加在标头中(我为此实现了一个拦截器)。验证在服务器上完成,并发回正确的响应(如果它被授权或未授权)。
  • 处理 JWT 时有用的页面:jwt.io
  • @MariusManastireanu 您从 Angular 发送的令牌与您从服务器收到的令牌相同?我正在做同样的事情...请帮助

标签: java json web token jwt


【解决方案1】:

【讨论】:

  • 请确保您使用的是最新版本。该库正在不断审查和改进,并且正在添加新算法/功能: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/…。我通过修改 com.nimbusds.jose.util.Base64Url 中的 byte[] decode() 方法来使用 android.util.Base64.decode 方法解决了这个问题。这是正确的方法吗?
  • 在 2.24 版中,我们从 Apache Commons Codec 切换到内部实用程序来处理 BASE64 编码 + 解码。至此,编解码器的 Android 问题已成功解决。有关详细信息,请参阅问题 bitbucket.org/connect2id/nimbus-jose-jwt/issue/63。另外,您现在应该获得更好的性能:)
  • @MariusManastireanu 您好,我从使用 JWT 的 angularjs + java 身份验证开始。您能否指导我或展示如何实现此目的的示例代码?
【解决方案2】:

这个库似乎运行良好:https://code.google.com/p/jsontoken/

这取决于谷歌番石榴。以下是 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

【讨论】:

  • 这个库使用了 1000 多个依赖项,所有这些都需要被解析、编译并且把你的项目弄得一团糟。为什么不主要使用 Java 中可用的功能?
  • 不是 1000s,它是少于 20s 的依赖关系,并且为我工作。
  • 这个库还活跃吗??
  • 无法下载项目。获得 401
【解决方案3】:

IETF 在其 wiki 上建议了 jose 库: http://trac.tools.ietf.org/wg/jose/trac/wiki

我强烈建议使用它们进行签名。我不是 Java 人,但看起来 jose4j 似乎是一个不错的选择。也有很好的例子:https://bitbucket.org/b_c/jose4j/wiki/JWS%20Examples

更新:jwt.io 提供了几个 jwt 相关的简洁比较 库及其功能。必须检查!

我很想听听其他 Java 开发者喜欢什么。

【讨论】:

【解决方案4】:

JJWT 旨在成为 JVM 和 Android 上最易于使用和理解的 JWT 库:

https://github.com/jwtk/jjwt

【讨论】:

  • 简单、简单、干净,立即生效。我首先选择了 Google JsonToken,并在处理了未解决的依赖项和代码页面以组装一个简单的 JWT 后切换到这里。
  • 如何防止令牌被盗?
  • “我如何处理令牌超时” - 根据 jwt 规范,您需要设置“exp”声明。 Jjwt 为此提供了一个方便的“setExpiration”方法。
  • 对此的警告。有很多人在运行时依赖解析方面遇到问题。我仍然不明白为什么作者不能像其他任何普通图书馆一样制作图书馆。我最终使用了其他东西,因为作者一直坚持认为配置错误。但我的设置与他在 GitHub 上所做的完全一样。将我的项目推迟了几天左右。
  • @LesHazlewood 我正在使用github.com/auth0/java-jwt,而且效果更好。没有奇怪的深奥地牢巫术。
【解决方案5】:

我发现这是小而完整的https://github.com/auth0/java-jwt

【讨论】:

    【解决方案6】:

    https://github.com/networknt/jsontoken

    这是原始 google jsontoken 的一个分支

    它自 2012 年 9 月 11 日以来一直没有更新,并且依赖于一些旧包。

    我做了什么:

    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 的作者。

    【讨论】:

      【解决方案7】:

      此页面保留了对各种语言(包括 Java)的实现的引用,并比较了功能:http://kjur.github.io/jsjws/index_mat.html

      【讨论】:

        【解决方案8】:

        如果您只需要解析未签名的未加密令牌,您可以使用以下代码:

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

        【讨论】:

          【解决方案9】:

          通过引用https://jwt.io/,您可以找到多种语言的jwt 实现,包括java。该网站还提供了这些实现之间的一些比较(它们支持的算法和......)。

          对于java,这些是提到的库:

          【讨论】:

          • 谢谢,它帮助我为我的游戏框架应用找到了 JWT 插件
          • 你们如何选择使用哪一个?如果所有 4 个库都涵盖了我的所有用例怎么办?
          • @jkerak 使用您认为最容易实现的任何一个。如果所有实现对您来说似乎都是希腊语,请使用名称最酷的那个。这对我来说效果很好。
          • @jkerak 我尝试了前两个库。 jjwt 库非常混乱,我花了几个小时进行测试,但仍然无法使其工作。 github.com/auth0/java-jwt 是最简单的,无需任何努力即可直接工作。强烈推荐github.com/auth0/java-jwt
          猜你喜欢
          • 2016-04-07
          • 2020-11-09
          • 1970-01-01
          • 2023-04-05
          • 2016-11-24
          • 2020-05-11
          • 2019-01-21
          • 1970-01-01
          • 2020-03-17
          相关资源
          最近更新 更多