zoukankan      html  css  js  c++  java
  • 【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

    当在复杂的环境中面临问题,格物之道需:浊而静之徐清,安以动之徐生。 云中,恰是如此!

  • 相关阅读:
    C#--C/S--学员管理系统--6--班级和下拉框的数据绑定
    C#--C/S--学员管理系统--5--通用验证类的设计和程序退出
    1046. 最后一块石头的重量
    1029. 两地调度
    1005. K 次取反后最大化的数组和
    944. 删列造序
    874. 模拟行走机器人
    860. 柠檬水找零
    map按值排序
    map按键排序
  • 原文地址:https://www.cnblogs.com/lulight/p/15428298.html
Copyright © 2011-2022 走看看