Ein mit AWS Amplify Authentication authentifizierter Client (https://aws-amplify.github.io/docs/js/authentication) sendet ein id_token an den Server und überprüft das id_token auf einem von Java implementierten Server.
TL;DR
IdTokenValidator.java
package com.exampleawsCognito.jwt;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.interfaces.RSAPublicKey;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import com.auth0.jwk.GuavaCachedJwkProvider;
import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
/**
*Behandeln Sie ID-Token, die Sie durch die Authentifizierung bei Cognito erhalten haben
*
* <p>
*Quelle, auf die zur Implementierung verwiesen wird
* https://github.com/awslabs/cognito-proxy-rest-service/blob/master/src/main/kotlin/com/budilov/cognito/services/CognitoService.kt
* </p>
*
* @ThreadSafe
*
*/
public class IdTokenValidator {
private static final String AWS_REGION = "ap-northeast-1";
private static final String AWS_COGNITO_USER_POOL_ID = "my_userpool_id";
private static JWTVerifier verifier = null;
private static long verifierExpire = -1;
private static final long VERIFIER_LIVE_MILISEC = 10 * 60 * 1000; //10 Minuten
private static final JWT JWT = new JWT();
/**
*Konstrukteur
*/
public IdTokenValidator() {
}
/**
*Überprüfen Sie das ID-Token
*
* @param idToken ID-Token, das überprüft werden soll
* @ID Token-Nutzdaten, wenn die Rückgabeüberprüfung erfolgreich ist
*
* @löst InvalidTokenException aus Die Authentifizierung ist fehlgeschlagen, da der ID-Token-Wert ungültig ist
*/
public DecodedJWT verify(String idToken) throws InvalidTokenException {
DecodedJWT decodedToken = JWT.decodeJwt(idToken);
//Stellen Sie sicher, dass es im Cognito-Benutzerpool signiert ist
String iss = decodedToken.getIssuer();
if (!jwtTokenIssuer().equals(iss)) {
throw new InvalidTokenException("Der Aussteller des ID-Tokens ist nicht das Zielsystem. iss=" + iss, idToken);
}
//Stellen Sie sicher, dass das ID-Token für "ID" verwendet wird.
String tokenUse = decodedToken.getClaim("token_use").asString();
if (!"id".equals(tokenUse)) {
throw new InvalidTokenException("Der Zweck des ID-Tokens ist nicht ID. Zeichen_use=" + tokenUse, idToken);
}
//Überprüfen Sie den Signaturalgorithmus.
String alg = decodedToken.getAlgorithm();
if (!"RS256".equals(decodedToken.getAlgorithm())) {
throw new InvalidTokenException("Der ID-Token-Signaturalgorithmus unterstützt dies nicht. alg=" + alg, idToken);
}
//Überprüfen Sie Nutzlast und Signatur.
DecodedJWT decodedJWT = null;
if ((decodedJWT = tokenVerify(decodedToken)) == null) {
throw new InvalidTokenException("ID-Token-Überprüfung fehlgeschlagen.", idToken);
}
return decodedJWT;
}
/**
*Führen Sie die Überprüfung mit der Bibliothek von auth0 durch
*
* @param kid ID Schlüssel-ID im Token-Header
* @Wenn return nicht null ist, wird das decodierte ID-Token angezeigt
*
* @löst InvalidTokenException aus Validierung fehlgeschlagen
*/
private DecodedJWT tokenVerify(DecodedJWT jwToken) throws InvalidTokenException {
try {
DecodedJWT verified = getVerifier(jwToken.getKeyId()).verify(jwToken);
return verified;
} catch (Exception e) {
throw new InvalidTokenException(e);
}
}
/**
*Holen Sie sich eine Instanz von JWTVerifier.
*
* <p>
*JWTVerifier ist ver.Da es ab 3 threadsicher wurde, wird es wiederverwendet.
*Es sollte jedoch regelmäßig aktualisiert werden, da das zum Signieren verwendete RSA-Schlüsselpaar möglicherweise aktualisiert wird.
* </p>
*
* @param kid Schlüssel-ID, die zum Signieren verwendet wird
*
* @return
*
* @throws MalformedURLException
* @throws JwkException
*/
private JWTVerifier getVerifier(String kid) throws MalformedURLException, JwkException {
if (verifier != null && System.currentTimeMillis() < verifierExpire) {
//Wenn es innerhalb des Ablaufdatums liegt, verwenden Sie es so wie es ist
return verifier;
}
synchronized (JWT) {
//Ich habe die Sperre erhalten, überprüfen Sie sie für alle Fälle erneut und erstellen Sie dann die Instanz
if (verifier != null && System.currentTimeMillis() < verifierExpire) {
return verifier;
}
UrlJwkProvider http = new UrlJwkProvider(new URL(jwksUrl()));
GuavaCachedJwkProvider provider = new GuavaCachedJwkProvider(http);
Jwk jwk = provider.get(kid);
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
verifier = JWT.require(algorithm)
.withIssuer(jwtTokenIssuer())
.build();
//Verlängern Sie die Lebensdauer von JWTVerifier
verifierExpire = System.currentTimeMillis() + VERIFIER_LIVE_MILISEC;
Calendar expire = GregorianCalendar.getInstance();
expire.setTimeInMillis(verifierExpire);
Logger.info("Erstellt eine Instanz von JWTVerifier. Frist"
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(expire.getTime()));
}
return verifier;
}
/**
*Holen Sie sich den Aussteller des ID-Tokens
*
* @return
*/
private String jwtTokenIssuer() {
return String.format("https://cognito-idp.%s.amazonaws.com/%s", AWS_REGION, AWS_COGNITO_USER_POOL_ID);
}
/**
*JSON-Web-Token(JWT)Holen Sie sich die URL des Sets.
*
* @return
*/
private String jwksUrl() {
return jwtTokenIssuer() + "/.well-known/jwks.json";
}
}
InvalidTokenException.class
public class InvalidTokenException extends Exception {
public InvalidTokenException(String message, String idToken) {
super(message + " token is " + idToken);
}
public InvalidTokenException(Throwable e) {
super(e);
}
}
Benutzerseite
try{
//TODO IdTokenValidator ist threadsicher, machen Sie es also statisch
DecodedJWT payload = new IdTokenValidator().verify(idToken);
String cognitoUserName = payload.getClaim("cognito:username").asString();
//Erforderliche Verarbeitung
}catch (InvalidTokenException e) {
Logger.error("Überprüfung des ID-Tokens fehlgeschlagen", e);
badRequest("Der Wert von IdToken ist ungültig");
}
Recommended Posts