控制台程序httpclient使用IdentityServer4的授权码模式获取token
系统使用IdentityServer4做鉴权,客户端是WPF,打算使用httpclient获取授权码模式的token,现做一个控制台测试例子。
获取token代码
新建一个AuthorizationCodeLogin类
using IdentityModel;
using IdentityModel.Client;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace ClientAuthorizationCode
{
public class AuthorizationCodeLogin
{
public async Task<string?> GetTokenAsync()
{
var webProxy = new WebProxy(new Uri("http://127.0.0.1:8080"));
var baseAddress = new Uri("https://localhost:5001");
var cookieContainer = new CookieContainer();
var handler = new HttpClientHandler()
{
UseCookies = true,
CookieContainer = cookieContainer,
};
var client = new HttpClient(handler);
var disco = await client.GetDiscoveryDocumentAsync(baseAddress.AbsoluteUri);
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return null;
}
#region 创建验证地址
var nonce = Convert.ToBase64String(CryptoRandom.CreateRandomKey(64));
var codeVerifier = GenerateCodeVerifier();
var codeChallenge = GenerateCodeChallenge(codeVerifier);
var ru = new RequestUrl(disco.AuthorizeEndpoint);
var url = ru.CreateAuthorizeUrl(
clientId: "apiClientCode",
responseType: "code",
redirectUri: "http://localhost:5002/signin-oidc",
nonce: nonce,
codeChallenge: codeChallenge,
codeChallengeMethod: "S256",
scope: "api1");
Console.WriteLine(url);
#endregion
#region 获取真正的登录地址,以及登录参数和Cookie
var response1 = await client.GetAsync(url);
if(!response1.IsSuccessStatusCode)
{
Console.WriteLine("获取真正的登录地址" + response1.StatusCode);
return null;
}
var result = await response1.Content.ReadAsStringAsync();
string temp = "<input name=\"__RequestVerificationToken\" type=\"hidden\" value=\"";
string temp1 = "\" /><input name=\"RememberLogin\"";
int startIndex = result.IndexOf(temp);
int endIndex = result.IndexOf(temp1);
int length = endIndex - startIndex - temp.Length;
string requestVerificationToken = result.Substring(startIndex + temp.Length, length);
Console.WriteLine(requestVerificationToken);
string temp2 = "<input type=\"hidden\" id=\"ReturnUrl\" name=\"ReturnUrl\" value=\"";
string temp3 = "code_challenge_method=S256";
int startIndex1 = result.IndexOf(temp2) + temp2.Length;
int endIndex1 = result.IndexOf(temp3);
int length1 = endIndex1 - startIndex1 + temp3.Length;
string returnUrl = result.Substring(startIndex1, length1).Replace("amp;", "");
Console.WriteLine(returnUrl);
var cookies = response1.Headers.GetValues(HeaderNames.SetCookie).ToList();
var cookieTemp = cookies[0].Split(';');
var cookieTemp1 = cookieTemp[0].Split('=');
#endregion
#region 登录,获取code
handler.CookieContainer.Add(baseAddress, new Cookie(cookieTemp1[0], cookieTemp1[1]));
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Username", "alice"),
new KeyValuePair<string, string>("Password", "Pass123$"),
new KeyValuePair<string, string>("ReturnUrl", returnUrl),
new KeyValuePair<string, string>("button", "login"),
new KeyValuePair<string, string>("__RequestVerificationToken", requestVerificationToken),
new KeyValuePair<string, string>("RememberLogin", "false"),
});
var response = await client.PostAsync(response1.RequestMessage.RequestUri, formContent);
Console.WriteLine(response.Headers.Location.AbsoluteUri);
Console.WriteLine(response.IsSuccessStatusCode);
Console.WriteLine(response.StatusCode);
if (!(response.StatusCode == HttpStatusCode.Found))
{
Console.WriteLine("获取Code失败"+response.StatusCode);
return null;
}
string code = response.Headers.Location.AbsoluteUri.Replace("http://localhost:5002/signin-oidc?code=", "").Replace("&scope=api1", "");
#endregion
#region 获取Token
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "apiClientCode",
ClientSecret = "secret345",
Code = code,
GrantType = "authorization_code",
RedirectUri = "http://localhost:5002/signin-oidc",
CodeVerifier = codeVerifier
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return null;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine(tokenResponse.AccessToken);
Console.WriteLine("\n\n");
return tokenResponse.AccessToken;
#endregion
}
private string GenerateCodeVerifier()
{
var rng = RandomNumberGenerator.Create();
var bytes = new byte[32];
rng.GetBytes(bytes);
var code_verifier = Convert.ToBase64String(bytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
return code_verifier;
}
private string GenerateCodeChallenge(string code_verifier)
{
var code_challenge = string.Empty;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(code_verifier));
code_challenge = Convert.ToBase64String(challengeBytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
return code_challenge;
}
}
}
}
拿到token后,访问API
为了方便测试,直接在Program.cs进行编写访问API的代码了,环境是.net 6,授权中心和API是使用IdentityServer4的官方案例,点击访问
using ClientAuthorizationCode;
using IdentityModel.Client;
using Newtonsoft.Json.Linq;
AuthorizationCodeLogin authorizationCodeLogin=new AuthorizationCodeLogin();
var token= authorizationCodeLogin.GetTokenAsync().GetAwaiter().GetResult();
var apiClient = new HttpClient();
apiClient.SetBearerToken(token);
var response3 = await apiClient.GetAsync("http://localhost:6011/identity");
if (!response3.IsSuccessStatusCode)
{
Console.WriteLine(response3.StatusCode);
}
else
{
var content = await response3.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
Console.ReadLine();
|