使用ADFS和OAuth 2生成JWT安全令牌

在现代应用程序开发中,保护用户凭据和数据安全至关重要。本文将介绍如何使用活动目录联合服务(ADFS)和OAuth 2协议来生成JSON Web Tokens(JWT)安全令牌,以增强应用程序的安全性。

应用程序池身份是一种安全机制,它允许应用程序在没有显式存储用户凭据的情况下运行。这是一种比在配置文件或其他资源文件中存储凭据更安全的方法。

使用代码

为了简化JWT令牌的生成过程,创建了一个名为“OAuth2 Authorization Provider 1.0.0”的NuGet包,并将其推送到了NuGet服务器。可以通过以下命令安装该包:

Install-Package OAuth2.Authorization

安装包后,需要在Web或应用程序配置文件中设置适当的ADFS OAuth 2配置,并调用上述NuGet包中的帮助函数。

获取JWT令牌的两种方式

本文将介绍两种获取JWT令牌的方法:Adal流和OAuth代码流。

首先,需要在应用程序的配置文件中添加全局应用程序设置。

<add key="AdfsInstance" value="" /> <add key="ClientId" value="" /> <add key="Resource" value="" /> <add key="RedirectUri" value="http://localhost:56194/redirect" /> <add key="AdfsAuthorityUrl" value="https://{0}/adfs/ls/{1}" />

提供上述配置值后,调用NuGet包中提供的以下函数:

AdfsAuthorization.GetAdfsOAuthJwtAccessTokenForWinAppUserUsingAdal()

该函数内部使用Microsoft.IdentityModel.Clients.ActiveDirectory NuGet包创建ADFS的认证上下文。认证上下文需要resource、clientId、redirectUri、adfsInstance和AuthorityUrl参数来为登录用户提供JWT令牌。

这种方法有两个步骤:

  1. 首先,需要根据clientId、resource和redirectUri(这些已经在ADFS服务器上为应用程序配置好了)从adfs服务器获取OAuth代码。
  2. 成功获取Auth代码后,需要再次将Auth代码交给ADFS服务器,以获取有关ADFS用户的JWT令牌。

同样,需要在应用程序的配置文件中添加全局应用程序设置。

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

提供上述配置后,调用NuGet包中的以下代码即可完成操作:

await AuthorizationManager.GetAdfsOAuthJwtAccessTokenForWinAppUserUsingAuthCodeAsync()

要获取auth代码,需要使用HttpClient请求并提供适当的ADFS认证URL。

完整代码示例

以下是使用Adal流获取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); }

以下是使用OAuth2授权代码流获取JWT令牌的完整代码示例:

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(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(); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485