【Azure Developer】如何验证 Azure AD的JWT Token (JSON Web 令牌)?
问题描述
使用微软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 <access_token>)访问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):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.16.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.18.0</version>
</dependency>
代码执行结果为:

在上面这段简单的代码中,也先后遇见了启动异常,主要是添加依赖时候少加了 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.<init>(JWTParser.java:24)
at com.auth0.jwt.impl.JWTParser.<init>(JWTParser.java:20)
at com.auth0.jwt.JWTDecoder.<init>(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.<init>(ObjectMapper.java:673)
at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:576)
at com.auth0.jwt.impl.JWTParser.getDefaultObjectMapper(JWTParser.java:64)
at com.auth0.jwt.impl.JWTParser.<init>(JWTParser.java:20)
at com.auth0.jwt.JWTDecoder.<init>(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
【Azure Developer】如何验证 Azure AD的JWT Token (JSON Web 令牌)?的更多相关文章
- android 中使用jwt token(json web token)--java
http://blog.csdn.net/mingzhnglei/article/details/51119836 下面贴上自己项目中的一个小小的example import com.nimbusds ...
- laravel JWT Auth - JSON Web令牌认证API
https://github.com/tymondesigns/jwt-auth/wiki
- 服务安全-JWT(JSON Web Tokens):百科
ylbtech-服务安全-JWT(JSON Web Tokens):百科 JSON Web Tokens是一种开放的行业标准 RFC 7519方法,用于在双方之间安全地表示索赔. JWT.IO允许您解 ...
- JSON Web令牌(JWT)介绍与使用
手机端接口开发会遇到一个问题是,接口登录后需要返回一个Token.token首先有一点必须唯一,每次请求都需要把token给带上.基于必须唯一的特性,很多朋友在开发是都选择了uuid.是不是token ...
- Go语言入门篇-jwt(json web token)权限验证
一.token.cookie.session的区别 1.cookie Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie. 内存Cookie由浏览器维护, ...
- JWT【JSON Web Token】 简述
draft: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html http://tools.ietf.org/html/ ...
- JWT(JSON Web Token) 多网站的单点登录,放弃session
多个网站之间的登录信息共享, 一种解决方案是基于cookie - session的登录认证方式,这种方式跨域比较复杂. 另一种替代方案是采用基于算法的认证方式, JWT(json web token) ...
- JWT(Json web token)认证详解
JWT(Json web token)认证详解 什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该to ...
- 理解JWT(JSON Web Token)认证及python实践
原文:https://segmentfault.com/a/1190000010312468?utm_source=tag-newest 几种常用的认证机制 HTTP Basic Auth HTTP ...
随机推荐
- 浅谈Java和Go的程序退出
前言 今天在开发中对Java程序的退出产生了困惑,因为题主之前写过一段时间Go,这两者的程序退出逻辑是不同的,下面首先给出结论,再通过简单的例子来介绍. 对于Java程序,Main线程退出,如果当前存 ...
- Windows内核基础知识-8-监听进程、线程和模块
Windows内核基础知识-8-监听进程.线程和模块 Windows内核有一种强大的机制,可以在重大事件发送时得到通知,比如这里的进程.线程和模块加载通知. 本次采用链表+自动快速互斥体来实现内核的主 ...
- 剑指 Offer 31. 栈的压入、弹出序列
剑指 Offer 31. 栈的压入.弹出序列 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如,序列 {1,2,3,4,5} 是某 ...
- 未能找到源类型“DbSet<T>”的查询模式的实现。未找到“Select”
使用EF6.0的模型优先模式进行开发,遇到了报错,如下图 后来发现是没引用using System.Linq; 引用后就不报错了
- nacos配置中心模块详解
本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 配置中心 业务上的配置,功能开关,服务治理上对弱依赖的降级,甚至数据库的密码等,都可能用到动态 ...
- 洛谷P1088——火星人(全排列+数学模拟)
题目描述 人类终于登上了火星的土地并且见到了神秘的火星人.人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法.这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学 ...
- 洛谷P1803——凌乱的yyy(贪心)
题目描述 现在各大oj上有n个比赛,每个比赛的开始.结束的时间点是知道的. yyy认为,参加越多的比赛,noip就能考的越好(假的) 所以,他想知道他最多能参加几个比赛. 由于yyy是蒟蒻,如果要参加 ...
- Java Web下MySQL数据库的增删改查(二)
前文:https://www.cnblogs.com/Arisf/p/14095002.html 在之前图书管理系统上做了改进优化 图书管理系统v2 首先是项目结构: 1.数据库的连接: 1 pack ...
- Java基础系列(9)- 数据类型扩展及常见面试题
整数拓展 // 整数拓展: 进制 二进制0b 十进制 八进制0 十六进制0x // 同一个数字在不同进制中,结果是不同的,进制换算 int i = 10; int i2 = 010; // 八进制 i ...
- python学习笔记(八)-模块
大型python程序以模块和包的形式组织.python标准库中包含大量的模块.一个python文件就是一个模块.1.标准模块 python自带的,不需要你安装的2.第三方模块 需要安装,别人提供的. ...