JSON Web Token (JWT) simple Examples

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.UUID;

//import org.jose4j.base64url.Base64;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.ErrorCodes;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;

public class OpenIDConnectMain {

	public static void main(String[] args) throws JoseException, MalformedClaimException {

		//生成 keyId,使用32位uuid即可。
		String keyId = UUID.randomUUID().toString().replaceAll("-", "");
		System.out.println("keyId=" + keyId);

		//生成公私钥对
		RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048);
		jwk.setKeyId(keyId);
		jwk.setAlgorithm(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);
		
		//公钥
		String publicKeyText = jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
		System.out.println("publicKeyText====="+publicKeyText);
		
		//私钥
		String privateKeyText = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
		System.out.println("privateKeyText====="+privateKeyText);
		
		//由私钥签名,生成token
		String idToken = createIdToken(keyId, privateKeyText);
		verifyIdToken(publicKeyText, idToken);
	}

	//使用公钥验证token签名,并解析出token中的内容。
	private static void verifyIdToken(String publicKeyText, String idToken)
			throws JoseException, MalformedClaimException {

		//解析出头信息(该操作不需要公钥)
		JsonWebSignature jwo = (JsonWebSignature) JsonWebSignature.fromCompactSerialization(idToken);
		String alg = jwo.getHeader("alg");
		
		//API网关中根据 kid 取得 publicKey(注册认证API时,配置了 keyId和publicKey)
		String kid = jwo.getHeader("kid");

		PublicKey publicKey = new RsaJsonWebKey(JsonUtil.parseJson(publicKeyText)).getPublicKey();

		JwtConsumer jwtConsumer = new JwtConsumerBuilder().setRequireExpirationTime() // the
				.setAllowedClockSkewInSeconds(30) // allow some leeway in
				.setRequireSubject() // the JWT must have a subject claim
				.setExpectedIssuer("Issuer") // whom the JWT needs to have been issued by
				.setExpectedAudience("Audience") // to whom the JWT is intended
				.setVerificationKey(publicKey) // verify the signature with the public key
				.setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
						new AlgorithmConstraints(ConstraintType.WHITELIST, alg))
				.build(); // create the JwtConsumer instance

		try {
			// Validate the JWT and process it to the Claims
			JwtClaims jwtClaims = jwtConsumer.processToClaims(idToken);
			System.out.println("JWT validation succeeded! " + jwtClaims);
		} catch (InvalidJwtException e) {
			// InvalidJwtException will be thrown, if the JWT failed processing
			// or validation in anyway.
			// Hopefully with meaningful explanations(s) about what went wrong.
			System.out.println("Invalid JWT! " + e);

			// Programmatic access to (some) specific reasons for JWT invalidity
			// is also possible
			// should you want different error handling behavior for certain
			// conditions.

			// Whether or not the JWT has expired being one common reason for
			// invalidity
			if (e.hasExpired()) {
				System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
			}

			// Or maybe the audience was invalid
			if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) {
				System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
			}
		}
	}

	// https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples
	private static String createIdToken(String keyId, String privateKeyText) throws JoseException {


		// claims
		JwtClaims claims = new JwtClaims();
		claims.setGeneratedJwtId();
		claims.setIssuedAtToNow();
		// expire time
		NumericDate date = NumericDate.now();
		date.addSeconds(120000);//1.3天
		claims.setExpirationTime(date);
		claims.setNotBeforeMinutesInThePast(1);
		claims.setIssuer("Issuer"); // who creates the token and signs it
		claims.setSubject("Subject");
		claims.setAudience("Audience");
		// 添加自定义参数
		claims.setClaim("UserKey1", "UserVal1");

		// jws
		JsonWebSignature jws = new JsonWebSignature();
		jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
		jws.setKeyIdHeaderValue(keyId);
		jws.setPayload(claims.toJson());
		PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyText)).getPrivateKey();
		jws.setKey(privateKey);
		System.out.println("createIdToken::jws=" + jws);

		// idToken
		String idToken = jws.getCompactSerialization();
		System.out.println("createIdToken::idToken=" + idToken);

		return idToken;
	}

}

 

The jose.4.j library is an open source (Apache 2.0) implementation of JWT 

 

id_token,也叫 ID Token,是在 OIDC 协议中定义的一种令牌。

id_token 生成需要 KeyPair, keyId 与 Claims。

OIDC对OAuth2最主要的扩展就是提供了ID Token。

ID Token是一个安全令牌,是一个授权服务器提供的包含用户信息(由一组Cliams构成以及其他辅助的Cliams)的JWT格式的数据结构。

ID Token的主要构成部分如下(使用OAuth2流程的OIDC)。

iss = Issuer Identifier:必须。提供认证信息者的唯一标识。一般是一个https的url(不包含querystring和fragment部分)。

sub = Subject Identifier:必须。iss提供的EU的标识,在iss范围内唯一。它会被RP用来标识唯一的用户。最长为255个ASCII个字符。

aud = Audience(s):必须。标识ID Token的受众。必须包含OAuth2的client_id。

exp = Expiration time:必须。过期时间,超过此时间的ID Token会作废不再被验证通过。

iat = Issued At Time:必须。JWT的构建的时间。

auth_time = AuthenticationTime:EU完成认证的时间。如果RP发送AuthN请求的时候携带max_age的参数,则此Claim是必须的。

nonce:RP发送请求的时候提供的随机字符串,用来减缓重放攻击,也可以来关联ID Token和RP本身的Session信息。

acr = Authentication Context Class Reference:可选。表示一个认证上下文引用值,可以用来标识认证上下文类。

amr = Authentication Methods References:可选。表示一组认证方法。

azp = Authorized party:可选。结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用。

猜你喜欢

转载自huangqiqing123.iteye.com/blog/2408012