65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使用 OAuth 2 从 ADFS C# 获取已登录用户或应用程序池标识用户的 Jwt Token

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2017年3月13日

CPOL

2分钟阅读

viewsIcon

21914

downloadIcon

599

如何使用 OAuth 2 从 ADFS 生成应用程序池标识或已登录用户的 Jwt 安全令牌

引言

在本文中,我们将学习如何使用 OAuth 2 从 ADFS 为应用程序池标识或已登录用户生成 Jwt 安全令牌。

背景

如果您有兴趣,请阅读应用程序池标识。现在,将用户凭据保存在任何配置文件或其他资源文件中有点危险。相反,我们可以使用应用程序池标识功能。

它比将凭据以任何形式存储在源代码中更安全。

请参阅此链接

Using the Code

我构建并将 NuGet 包推送到 NuGet 服务器,名为“OAuth2 Authorization Provider 1.0.0”,它已完成所有前期工作。

用于安装软件包的 NuGet 包管理器命令:Install-Package OAuth2.Authorization

您需要做的就是将适当的 ADFS OAuth 2 配置放在 web 或 app 配置文件中,并从上面提到的 NuGet 包中调用辅助函数。

有关 OAuth 2 规范的更多信息,请访问此链接

在本文中,我们使用两种方法来获取 JWT 令牌。它们是

  • Adal 流
  • OAuth 代码流

方法 1:使用 Adal 流

配置依赖项

<!--Global appSettings-->
<add key="AdfsInstance" value=""/>
<add key="ClientId" value=""/>
<add key="Resource" value=""/>
<add key="RedirectUri" value="https://:56194/redirect"/>

<add key="AdfsAuthorityUrl" value="https://{0}/adfs/ls/{1}" />

appSettings 部分中提供上述配置值,并调用“OAuth2 Authorization Provider 1.0.0” NuGet 包中提供的以下函数调用。

AdfsAuthorization.GetAdfsOAuthJwtAccessTokenForWinAppUserUsingAdal()

工作原理

在内部,它使用 Microsoft.IdentityModel.Clients.ActiveDirectory NuGet 包为 ADFS 创建身份验证上下文。身份验证上下文需要 resourceclientIdredirectUriadfsInstanceAuthorityUrl 参数,以便为已登录用户提供 Jwt 令牌。

完整代码

if (!AdfsConfiguration.IsInitialized) throw new SecurityException
         (Constants.AdfsConfigurationInitilizationExceptionMessage);
if (string.IsNullOrEmpty(AdfsConfiguration.AdfsAuthorityUrl)) 
         throw new SecurityException
         (Constants.AdfsConfigurationAdfsAuthorityUrlInitilizationExceptionMessage);

try
{
     var authenticationContext = new AuthenticationContext
         (string.Format(AdfsConfiguration.AdfsAuthorityUrl, AdfsConfiguration.AdfsInstance, 
          AdfsConfiguration.Resource), false);

     var asyncRequest = authenticationContext.AcquireTokenAsync
         (AdfsConfiguration.Resource, AdfsConfiguration.ClientId, 
          new Uri(AdfsConfiguration.RedirectUri), new PlatformParameters(PromptBehavior.Auto));
     var accessToken = asyncRequest.Result.AccessToken;
     return accessToken;
}
catch (Exception exp)
{
    var additionalInfo = $" additionalInfo : [authenticationContext : 
       {string.Format(AdfsConfiguration.AdfsAuthorityUrl, AdfsConfiguration.AdfsInstance, 
        AdfsConfiguration.Resource)}]";
    throw new SecurityException
       ($"AdfsAuthorization.GetAdfsOAuthJwtAccessTokenForWinAppUserUsingAdal is failed, 
       {additionalInfo}", exp);
}

方法 2:使用 OAuth2 授权代码流

它有两个过程

  • 首先,我们需要根据 clientIdresourceredirecturi(已在 ADFS 服务器中为应用程序配置)从 ADFS 服务器获取 OAuth 代码。
  • 成功从 ADFS 获取 Auth 代码后,我们必须再次将 Auth 代码交给 ADFS 服务器,以为相关的 ADFS 用户提供 Jwt 令牌。

配置依赖项

<!--Global-->
<add key="AdfsInstance" value=""/>
<add key="ClientId" value=""/>
<add key="Resource" value=""/>
<add key="RedirectUri" value="https://:56194/redirect"/>
<add key="AdfsTokenServiceUrl" value="https://{0}/adfs/oauth2/token/"/>

appSetting 部分中提供上述配置,并调用以下代码行,您就完成了(已在 NuGet 包中提供)。

await AuthorizationManager.GetAdfsOAuthJwtAccessTokenForWinAppUserUsingAuthCodeAsync()

工作原理

要获取授权码,您需要通过提供正确的 ADFS 身份验证 URL 来发出 HttpClient 请求。

完整代码

var authUrl = string.Format(AdfsConfiguration.AdfsAuthUrl, AdfsConfiguration.AdfsInstance, 
              AdfsConfiguration.ClientId, AdfsConfiguration.Resource, 
              AdfsConfiguration.UrlEncodedRedirectUri);
    var authCode = "";
    
    try
    {
        do
        {
            var result = await Client.GetAsync(authUrl);
            await result.Content.ReadAsStringAsync();
            IEnumerable<string> values;
            if (result.Headers.TryGetValues("location", out values))
            {
                foreach (string s in values)
                {
                    if (s.Contains("code="))
                    {
                        authUrl = "";
                        authCode = s.Substring(s.IndexOf("code=", StringComparison.Ordinal) + 5);
                    }
                    else
                    {
                        authUrl = s;
                    }
                }
            }
            else
            {
                authUrl = "";
            }
        } while (!string.IsNullOrEmpty(authUrl));
        
        return authCode;
    }
    catch (Exception exp)
    {
        var additionalInfo = $"additionalInfo : [authUrl: {authUrl}]";
        throw new SecurityException
        ($"AdfsAuthorization.GetAuthCodeForWinAppUserAsync is failed, {additionalInfo}", exp);
    }

获得 Authcode 后,使用 WebClient 获取加密格式的 Jwt 令牌。 如果你想看看它包含什么,那么你需要有适当的证书来验证和查看它有什么。 NuGet 中也提供了此功能。

完整代码

var client = new WebClient();
    try
    {
        if (AdfsConfiguration.UseProxy == "Y")
        {
            var proxyObject = new WebProxy("Proxy", 80) 
            { Credentials = CredentialCache.DefaultNetworkCredentials };
            client.Proxy = proxyObject;
        }
        
        //Uri address = new Uri(String.Format("https://{0}/adfs/oauth2/token/", AdfsInstance));
        Uri address = new Uri
        (string.Format(AdfsConfiguration.AdfsTokenServiceUrl, AdfsConfiguration.AdfsInstance));
        
        Uri redirectAddress = new Uri(AdfsConfiguration.RedirectUri);
        
        NameValueCollection values = new NameValueCollection
        {
            {"client_id", AdfsConfiguration.ClientId},
            {"grant_type", "authorization_code"},
            {"code", code},
            {"redirect_uri", redirectAddress.ToString()}
        };
        
        byte[] responseBytes = client.UploadValues(address, "POST", values);
        
        string response = System.Text.Encoding.UTF8.GetString(responseBytes);
        
        return response;
        
    }
    catch (Exception exp)
    {
        var additionalInfo = $" additionalInfo : 
        [address: {string.Format(AdfsConfiguration.AdfsTokenServiceUrl, 
        AdfsConfiguration.AdfsInstance) }, redirect Uri :{AdfsConfiguration.RedirectUri}]";
        throw new SecurityException($"AdfsAuthorization.GetAdfsOAuthTokenByAuthCode is failed, 
        {additionalInfo}", exp);
    }
    finally
    {
        client.Dispose();
    }

这就是从 ADFS 服务器为已登录用户或应用程序池标识获取 Jwt 令牌的全部内容。

除了本文之外,我还附加了示例测试工具来进行试用(Winform - 测试应用程序)。

历史

  • 版本 1.0.1
© . All rights reserved.