问题描述

使用微软Azure AD,对授权进行管理。通过所注册应用的OAuth API(https://login.chinacloudapi.cn/{TENANT ID}/oauth2/v2.0/token),已经获取到Token,但是如何在应用端对Token进行验证呢?

 

问题场景类似于:一个基于 Java 的API服务,使用Azure AD生产的access_token来做为客户端访问API服务的身份验证。

步骤如下:

客户端申请AAD的access_token

客户端在header里添加Authorization参数(值为Bearer )访问API

服务端在收到header里的token后,验证此token是否有效。若有效则进行具体的业务数据处理;若无效,则返回认证失败

问题是: 在Java代码中如何来验证这个Token是否有效呢?

 

问题解决

在验证JWT的关键问题中,是需要获取到生产Token时候的公钥密钥。因为 Azure AD 使用一组私钥签署JWT Token访问令牌,并在 JWKS URI 提供相应的公共密钥。

第一步:通过Azure AD 的 openid-configuration 终结点,可以获取到 JWKS URI,中国区公用的JWKS URI 为:  https://login.partner.microsoftonline.cn/common/discovery/keys ,获取方式见下图:

 

第二步:在代码中,直接使用JWKS URI来解析公钥密钥,然后生成  RSA256 Algorithm 对象,以下为代码片段:

URL keysURL = new URL("https://login.partner.microsoftonline.cn/common/discovery/keys");

JwkProvider provider = new UrlJwkProvider(keysURL);

Jwk jwk = provider.get(jwt.getKeyId());

Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);

algorithm.verify(jwt);

 

全部的Java 代码:

package jwttest;

import java.net.MalformedURLException;

import java.net.URL;

import java.security.interfaces.RSAPublicKey;

import java.util.*;

import com.auth0.jwk.Jwk;

import com.auth0.jwk.JwkException;

import com.auth0.jwk.JwkProvider;

import com.auth0.jwk.UrlJwkProvider;

import com.auth0.jwt.JWT;

import com.auth0.jwt.algorithms.Algorithm;

import com.auth0.jwt.exceptions.SignatureVerificationException;

import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Scanner;

public class Main {

public static void main(String[] args) {

System.out.println("Start to verify the AAD TOken...");

// Using Scanner for Getting Input from User

Scanner in = new Scanner(System.in);

String stoken = in.nextLine();

System.out.println("You entered Token is :: " + stoken);

if (stoken.length() < 50) {

stoken = "eyJ0eXAiOiJKV1QiLCJhbGciO......................_-dIQ";

System.out.println("You entered Token is too short, use the default value :: " + stoken);

}

DecodedJWT jwt = JWT.decode(stoken);

System.out.println("JWT Key ID is : " + jwt.getKeyId());

JwkProvider provider = null;

Jwk jwk = null;

Algorithm algorithm = null;

try {

URL keysURL = new URL("https://login.partner.microsoftonline.cn/common/discovery/keys");

provider = new UrlJwkProvider(keysURL);

jwk = provider.get(jwt.getKeyId());

algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);

algorithm.verify(jwt);

// if the token signature is invalid, the method will throw

// SignatureVerificationException

System.out.println("JWT Validation completed.");

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (JwkException e) {

e.printStackTrace();

} catch (SignatureVerificationException e) {

System.out.println(e.getMessage());

}

}

}

需要添加的依赖有(pom.xml):

com.fasterxml.jackson.core

jackson-core

2.13.0

com.fasterxml.jackson.core

jackson-databind

2.13.0

com.fasterxml.jackson.core

jackson-annotations

2.13.0

com.auth0

java-jwt

3.16.0

com.auth0

jwks-rsa

0.18.0

代码执行结果为:

 

 

在上面这段简单的代码中,也先后遇见了启动异常,主要是添加依赖时候少加了 com.fasterxml.jackson.core,并且需要保持版本的一致性。否则,会依次遇见如下错误:

错误一:java.lang.ClassNotFoundException: com.fasterxml.jackson.core.exc.InputCoercionException  

Exception in thread "main" java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/exc/InputCoercionException

at com.auth0.jwt.impl.JWTParser.addDeserializers(JWTParser.java:58)

at com.auth0.jwt.impl.JWTParser.(JWTParser.java:24)

at com.auth0.jwt.impl.JWTParser.(JWTParser.java:20)

at com.auth0.jwt.JWTDecoder.(JWTDecoder.java:32)

at com.auth0.jwt.JWT.decode(JWT.java:45)

at blob.Main.main(Main.java:36)

Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.core.exc.InputCoercionException

at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)

at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)

at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)

... 6 more

 

错误二:java.lang.ClassNotFoundException: com.fasterxml.jackson.core.util.JacksonFeature

Exception in thread "main" java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/util/JacksonFeature

at com.fasterxml.jackson.databind.ObjectMapper.(ObjectMapper.java:673)

at com.fasterxml.jackson.databind.ObjectMapper.(ObjectMapper.java:576)

at com.auth0.jwt.impl.JWTParser.getDefaultObjectMapper(JWTParser.java:64)

at com.auth0.jwt.impl.JWTParser.(JWTParser.java:20)

at com.auth0.jwt.JWTDecoder.(JWTDecoder.java:32)

at com.auth0.jwt.JWT.decode(JWT.java:45)

at blob.Main.main(Main.java:36)

Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.core.util.JacksonFeature

at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)

at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)

at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)

... 7 more

只要在引入 jackson-core,jackson-databind,jackson-annotations 时保持版本一直即可解决以上问题。如本示例中使用的版本为:2.13.0

 

 

Java 应用验证Azure AD的 Token演示动画:

 

 

参考资料

Azure Active Directory Token Validation in Java Applications : https://sgonzal.com/2020/04/06/jwt-validation.html#:~:text=Set%20up%20the%20clients%20that%20call%20the%20web,tokens%20issued%20by%20AAD%20in%20a%20Java%20application.

How can I validate an Azure AD JWT Token in Java? : https://stackoverflow.com/questions/60884823/how-can-i-validate-an-azure-ad-jwt-token-in-java

 

好文链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。