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

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

  • 相关阅读:
    BZOJ 1391: [Ceoi2008]order
    BZOJ 4504: K个串
    2019 年百度之星·程序设计大赛
    POJ 2398 Toy Storage (二分 叉积)
    POJ 2318 TOYS (二分 叉积)
    HDU 6697 Closest Pair of Segments (计算几何 暴力)
    HDU 6695 Welcome Party (贪心)
    HDU 6693 Valentine's Day (概率)
    HDU 6590 Code (判断凸包相交)
    POJ 3805 Separate Points (判断凸包相交)
  • 原文地址:https://www.cnblogs.com/lulight/p/15428298.html
Copyright © 2011-2022 走看看