有没有java aws cognito API 的JWT 验证示例?

Is there any java example of verification of JWT for aws cognito API?

我正在使用 aws cognito 用户池,在用户登录后,我在我的单页应用程序中获得了一个 ID 令牌,这是预期的,然后对于每个请求,我需要在我的后端休息验证 ID 令牌 API,也就是在java中,aws doc并没有过多提及如何去做。

有例子吗?

混淆包括:

  1. id token 好像不仅仅是一个签名的JWT,它也是加密的,在使用nimbus 库时,我需要为加密的JWT 指定一个secret,我在哪里可以得到这个secret?我的理解是这应该来自 aws,我需要下载一些东西然后放入我的 jvm 密钥库吗?

  2. 有个著名的jwts.json可以从aws上下载,貌似:

`

{
    "keys": [
        {
            "alg": "RS256",
            "e": "AQAB",
            "kid": "HFPWHdsrG5WyulOwH5dai69YTsWz2KBB1NHbAcVx7M0=",
            "kty": "RSA",
            "n": "...",
            "use": "sig"
        },
        {
            "alg": "RS256",
            "e": "AQAB",
            "kid": "kSwTdVq/qD4Ra4Q8dJqUTlvOA7eiLxezOZ3mJKI61zU=",
            "kty": "RSA",
            "n": "....",
            "use": "sig"
        }
    ]
}

`

这个怎么理解,每个属性有什么用?是不是用户池中的每个用户都代表一个key?

  1. 是否有 aws cognito 服务验证的示例 java 代码,我可以使用 aws sdk 还是必须使用像 nimbus 这样的库来自己进行验证?

您可以使用标准 JWT library 验证令牌。此外,还有几个步骤涉及验证 JWT 令牌。虽然我找不到 Java 示例,但以下是一个 NodeJS 示例,它将解释验证过程。

const jwt = require('jsonwebtoken');
const jwtToken = "sampletoken****";
const jwkPem = { "alg" : "RS256", "kid" : "samplekid****" }

var decodedJwt = jwt.decode(jwtToken, {complete: true});

//Fail if the token is not jwt
if (!decodedJwt) {
    console.log("Not a valid JWT token");
    return;
}

//Fail if token is not from your User Pool
if (decodedJwt.payload.iss != iss) {
    console.log("invalid issuer");
    return;
}

//Reject the jwt if it's not an 'Access Token'
if (!(decodedJwt.payload.token_use == 'id' || 
    decodedJwt.payload.token_use == 'access')) {
    console.log("token_use is invalid");
    return;
}

//Get the kid from the token and retrieve corresponding PEM
var kid = decodedJwt.header.kid;
var pem = jwkPem[kid];
if (!pem) {
    console.log("Invalid access token");
    return;
}

//Verify the signature of the JWT token to ensure it's really coming from your User Pool and that it has not expired
jwt.verify(jwtToken, pem, { issuer: iss, maxAge: 3600000}, function(err, payload) {
  if(err) {
    console.log(err);
  } else {
    console.log("Authorization successful");
  }
});

至于秘密,你指的是App Client特有的吗?这是您在创建 App Client 时获得的。转到 AWS 控制台和 Cognito。选择适当的用户池,单击 App Client。这是秘密,但您必须确保在创建 App Client 时 select 有创建它的选项(或者不使用)。否则,重新制作一个。

我只是为此苦苦挣扎并想分享它。

如果您使用 Maven,请将此添加到您的 pom.xml

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>jwks-rsa</artifactId>
    <version>0.4.0</version>
</dependency>

如果你使用gradle添加

compile 'com.auth0:jwks-rsa:0.4.0'
compile 'com.auth0:java-jwt:3.3.0'

创建一个实现 RSAKeyProvider

的 class
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.JwkProviderBuilder;
import com.auth0.jwt.interfaces.RSAKeyProvider;

import java.net.MalformedURLException;
import java.net.URL;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

public class AwsCognitoRSAKeyProvider implements RSAKeyProvider {

    private final URL aws_kid_store_url;
    private final JwkProvider provider;

    public AwsCognitoRSAKeyProvider(String aws_cognito_region, String aws_user_pools_id) {
        String url = String.format("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", aws_cognito_region, aws_user_pools_id);
        try {
            aws_kid_store_url = new URL(url);
        } catch (MalformedURLException e) {
            throw new RuntimeException(String.format("Invalid URL provided, URL=%s", url));
        }
        provider = new JwkProviderBuilder(aws_kid_store_url).build();
    }


    @Override
    public RSAPublicKey getPublicKeyById(String kid) {
        try {
            return (RSAPublicKey) provider.get(kid).getPublicKey();
        } catch (JwkException e) {
            throw new RuntimeException(String.format("Failed to get JWT kid=%s from aws_kid_store_url=%s", kid, aws_kid_store_url));
        }
    }

    @Override
    public RSAPrivateKey getPrivateKey() {
        return null;
    }

    @Override
    public String getPrivateKeyId() {
        return null;
    }
}

现在您可以通过

验证您的令牌
String aws_cognito_region = "us-east-1"; // Replace this with your aws cognito region
String aws_user_pools_id = "us-east-1_7DEw1nt5r"; // Replace this with your aws user pools id
RSAKeyProvider keyProvider = new AwsCognitoRSAKeyProvider(aws_cognito_region, aws_user_pools_id);
Algorithm algorithm = Algorithm.RSA256(keyProvider);
JWTVerifier jwtVerifier = JWT.require(algorithm)
    //.withAudience("2qm9sgg2kh21masuas88vjc9se") // Validate your apps audience if needed
    .build();

String token = "eyJraWQiOiJjdE.eyJzdWIiOiI5NTMxN2E.VX819z1A1rJij2"; // Replace this with your JWT token
jwtVerifier.verify(token);

请注意,JwkProviderBuilder 将构建一个具有 LRU 缓存的 JwkProvider,该缓存缓存从 aws 密钥存储中检索到的密钥,这非常简洁!可以使用构建器更改缓存规则。

[更新] 将创建 JwkProvider 移动到构造函数,因此缓存受到尊重,正如@danieln 评论的那样