使用 jwt 令牌认证识别用户

Posted

技术标签:

【中文标题】使用 jwt 令牌认证识别用户【英文标题】:Identifying the user using jwt token authentication 【发布时间】:2016-04-06 04:55:11 【问题描述】:

我正在使用 jersey rest webservice 以及带有 RSA 签名令牌功能的 JWT 进行身份验证。我能够成功创建令牌并将其发送到前端。现在,在我做到这一点之后,我对验证令牌以及识别请求资源的用户感到困惑。

这里有几个问题:

    我是否必须解码前端收到的 jwt 令牌以检查 索赔? 如何识别请求后端资源的用户?

因为在 SO 上的一些帖子中,有些人说不需要在前端解码令牌 (check this link),而其他站点上的其他示例显示了在前端解码令牌的示例,例如 this

现在我很困惑如何进一步了解我应该在前端实际解码令牌还是保持原样?如果是这样,其他示例如何在前端显示解码,例如this:

angular.module('app')
   .factory('Auth', ['$http', '$localStorage', 'urls', function ($http, $localStorage, urls) 
       function urlBase64Decode(str) 
           var output = str.replace('-', '+').replace('_', '/');
           switch (output.length % 4) 
               case 0:
                   break;
               case 2:
                   output += '==';
                   break;
               case 3:
                   output += '=';
                   break;
               default:
                   throw 'Illegal base64url string!';
           
           return window.atob(output);
       

       function getClaimsFromToken() 
           var token = $localStorage.token;
           var user = ;
           if (typeof token !== 'undefined') 
               var encoded = token.split('.')[1];
               user = JSON.parse(urlBase64Decode(encoded));
           
           return user;
       

我在这里使用的令牌示例:

private void authenticate(String email, String password)
    throws Exception 
try 
    Connection con = DBConnection.getConnection();
    PreparedStatement statement = con.prepareStatement("select USR_PRIMARY_EMAIL, USR_PASSWORD from TBL_USER where USR_PRIMARY_EMAIL=? and USR_PASSWORD=?");
    statement.setString(1, email);
    statement.setString(2, password);
    ResultSet result = statement.executeQuery();
    if (result.next()) 
        System.out.println("User authenticated successfully");

        KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
        keyGenerator.initialize(1024);

        KeyPair kp = keyGenerator.genKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();
        JWSSigner signer = new RSASSASigner(privateKey);

        JWTClaimsSet claimsSet = new JWTClaimsSet();
        claimsSet.setSubject("alice");
        claimsSet.setIssuer("https://c2id.com");
        claimsSet.setExpirationTime(new Date(new Date().getTime() + 60 * 1000));

        System.out.println("publicKey is: " + publicKey);
        System.out.println("privateKey is: " + privateKey);
        System.out.println("claimsSet is: " + claimsSet);

        SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256),claimsSet);

        signedJWT.sign(signer);
        token = signedJWT.serialize();
        System.out.println("Token is: " + token);

        signedJWT = SignedJWT.parse(token);

        System.out.println("signedJWT is: " + signedJWT);

        JWSVerifier verifier = new RSASSAVerifier(publicKey);
        assertTrue(signedJWT.verify(verifier));
        assertEquals("alice", signedJWT.getJWTClaimsSet().getSubject());
        assertEquals("https://c2id.com", signedJWT.getJWTClaimsSet().getIssuer());
        assertTrue(new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime()));
     else 
        System.out.println("User doesn't exist");
    
 catch (Exception e) 
    System.out.println("DB related Error");
    e.printStackTrace();


还有一个问题是使用 nimbus+jose_JWT(RSA 签名) 生成的令牌我无法在角度 auth0 库中解码。是因为我使用了公钥吗?

【问题讨论】:

【参考方案1】:

我是否必须解码前端收到的 jwt 令牌以检查声明?

是的。 JWT 声明集是 base64URL 编码的 JSON,因此您需要解码才能读取它。


如何识别请求后端资源的用户?

sub 声明是可选的,但实际上每个 JWT 提供者都会发布所有令牌,其主题 ID 可标识请求者。来自 JWT 规范:

“子”(主体)声明标识了作为主体的主体 JWT 的主题。 JWT 中的声明通常是语句 关于主题。主题值必须被限定为 在发行人的上下文中是本地唯一的,或者是全球唯一的。 此声明的处理通常是特定于应用程序的。这 "sub" 值是包含 StringOrURI 的区分大小写的字符串 价值。使用此声明是可选的。


还有一个问题是使用 nimbus+jose_JWT(RSA 签名) 生成的令牌我无法在 angular auth0 库中解码。是因为我使用的是公钥吗?

没有。所有 JWT 声明集都是 base64URL 编码的 JSON,独立于签名方法,因此您应该能够对其进行解码。

【讨论】:

嗨罗德里戈。非常感谢您的回答。我现在可以正确解码令牌,但问题是它只返回有效负载信息,例如subissexp,但没有标头和签名?我是否也需要这些值来识别用户?并将授权信息插入标题?还是只有有效载荷信息就足够了? 调用接受 JWT 的 API 时,您只需在 Authorization 标头中传递整个 JWT(例如 Authorization: Bearer eyJ...)。 JWT 库将允许您在解码令牌时访问声明集。访问标头通常是不必要的,因为您应该先验地知道您期望的签名算法(请参阅auth0.com/blog/2015/03/31/…)。也不需要手动访问签名,因为您应该直接使用库的验证方法。

以上是关于使用 jwt 令牌认证识别用户的主要内容,如果未能解决你的问题,请参考以下文章

JSON Web 令牌 - 如何识别用户?

Lua识别Jwt令牌业务

Lua识别Jwt令牌业务

如何使用访问和刷新令牌返回自定义数据以识别 Django Rest Framework 简单 JWT 中的用户?

如何使用 auth0 jwt 识别用户

.NET Core 2 Web API JWT 令牌无法识别