【Azure Developer】如何验证 Azure AD的JWT Token (JSON Web 令牌)?

简介: 【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服务的身份验证。

步骤如下:

  1. 客户端申请AAD的access_token
  2. 客户端在header里添加Authorization参数(值为Bearer <access_token>)访问API
  3. 服务端在收到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-corejackson-databindjackson-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

 

相关文章
|
1月前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
224 1
|
1月前
【Azure App Service】PowerShell脚本批量添加IP地址到Web App允许访问IP列表中
Web App取消公网访问后,只允许特定IP能访问Web App。需要写一下段PowerShell脚本,批量添加IP到Web App的允许访问IP列表里!
|
3月前
|
存储 中间件 API
ThinkPHP 集成 jwt 技术 token 验证
本文介绍了在ThinkPHP框架中集成JWT技术进行token验证的流程,包括安装JWT扩展、创建Token服务类、编写中间件进行Token校验、配置路由中间件以及测试Token验证的步骤和代码示例。
ThinkPHP 集成 jwt 技术 token 验证
|
2月前
|
JSON 数据格式 Python
Python编程:利用JSON模块编程验证用户
Python编程:利用JSON模块编程验证用户
29 1
|
3月前
|
JSON 安全 数据安全/隐私保护
从0到1搭建权限管理系统系列三 .net8 JWT创建Token并使用
【9月更文挑战第22天】在.NET 8中,从零开始搭建权限管理系统并使用JWT(JSON Web Tokens)创建Token是关键步骤。JWT是一种开放标准(RFC 7519),用于安全传输信息,由头部、载荷和签名三部分组成。首先需安装`Microsoft.AspNetCore.Authentication.JwtBearer`包,并在`Program.cs`中配置JWT服务。接着,创建一个静态方法`GenerateToken`生成包含用户名和角色的Token。最后,在控制器中使用`[Authorize]`属性验证和解析Token,从而实现身份验证和授权功能。
263 3
|
4月前
【Azure Durable Function】PowerShell Activity 函数遇见 Newtonsoft.Json.JsonReaderException: The reader's MaxDepth of 64 has been exceeded.
【Azure Durable Function】PowerShell Activity 函数遇见 Newtonsoft.Json.JsonReaderException: The reader's MaxDepth of 64 has been exceeded.
|
4月前
【Azure Web Job】Azure Web Job执行Powershell脚本报错 The term 'Select-AzContext' is not recognized as the name
【Azure Web Job】Azure Web Job执行Powershell脚本报错 The term 'Select-AzContext' is not recognized as the name
|
4月前
|
存储 SQL JSON
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
|
4月前
|
API
【Azure Developer】记录一段验证AAD JWT Token时需要设置代理获取openid-configuration内容
【Azure Developer】记录一段验证AAD JWT Token时需要设置代理获取openid-configuration内容
|
4月前
|
JSON 开发工具 数据格式
【Azure Event Hub】Event Hub的Process Data页面无法通过JSON格式预览数据
【Azure Event Hub】Event Hub的Process Data页面无法通过JSON格式预览数据