feat: 新增Oauth鉴权模块,支持qq登录、gitee登录
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
|
||||
|
||||
/// <summary>
|
||||
/// Contains constants specific to the <see cref="GiteeAuthenticationHandler"/>.
|
||||
/// </summary>
|
||||
public static class GiteeAuthenticationConstants
|
||||
{
|
||||
public static class Claims
|
||||
{
|
||||
public const string Url = "urn:gitee:url";
|
||||
public const string AvatarUrl = "urn:gitee:avatarUrl";
|
||||
|
||||
public const string OpenId = "urn:openid";
|
||||
public const string AccessToken = "urn:access_token";
|
||||
public const string Name = "urn:name";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
|
||||
|
||||
/// <summary>
|
||||
/// Default values used by the Gitee authentication middleware.
|
||||
/// </summary>
|
||||
public static class GiteeAuthenticationDefaults
|
||||
{
|
||||
/// <summary>
|
||||
/// Default value for <see cref="AuthenticationScheme.Name"/>.
|
||||
/// </summary>
|
||||
public const string AuthenticationScheme = "Gitee";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
|
||||
/// </summary>
|
||||
public static readonly string DisplayName = "Gitee";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
|
||||
/// </summary>
|
||||
public static readonly string Issuer = "Gitee";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
|
||||
/// </summary>
|
||||
public static readonly string CallbackPath = "/signin-gitee";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string TokenEndpoint = "https://gitee.com/oauth/token";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string UserInformationEndpoint = "https://gitee.com/api/v5/user";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="GiteeAuthenticationOptions.UserEmailsEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string UserEmailsEndpoint = "https://gitee.com/api/v5/emails";
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods to add Gitee authentication capabilities to an HTTP application pipeline.
|
||||
/// </summary>
|
||||
public static class GiteeAuthenticationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddGitee([NotNull] this AuthenticationBuilder builder)
|
||||
{
|
||||
return builder.AddGitee(GiteeAuthenticationDefaults.AuthenticationScheme, options => { });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddGitee(
|
||||
[NotNull] this AuthenticationBuilder builder,
|
||||
[NotNull] Action<GiteeAuthenticationOptions> configuration)
|
||||
{
|
||||
return builder.AddGitee(GiteeAuthenticationDefaults.AuthenticationScheme, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <param name="scheme">The authentication scheme associated with this instance.</param>
|
||||
/// <param name="configuration">The delegate used to configure the Gitee options.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddGitee(
|
||||
[NotNull] this AuthenticationBuilder builder,
|
||||
[NotNull] string scheme,
|
||||
[NotNull] Action<GiteeAuthenticationOptions> configuration)
|
||||
{
|
||||
return builder.AddGitee(scheme, GiteeAuthenticationDefaults.DisplayName, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds <see cref="GiteeAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables Gitee authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <param name="scheme">The authentication scheme associated with this instance.</param>
|
||||
/// <param name="caption">The optional display name associated with this instance.</param>
|
||||
/// <param name="configuration">The delegate used to configure the Gitee options.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddGitee(
|
||||
[NotNull] this AuthenticationBuilder builder,
|
||||
[NotNull] string scheme,
|
||||
[CanBeNull] string caption,
|
||||
[NotNull] Action<GiteeAuthenticationOptions> configuration)
|
||||
{
|
||||
return builder.AddScheme<GiteeAuthenticationOptions, GiteeAuthenticationHandler>(scheme, caption, configuration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
|
||||
using static Yi.Framework.AspNetCore.Authentication.OAuth.Gitee.GiteeAuthenticationConstants;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee
|
||||
{
|
||||
public class GiteeAuthenticationHandler : OauthAuthenticationHandler<GiteeAuthenticationOptions>
|
||||
{
|
||||
public GiteeAuthenticationHandler(IOptionsMonitor<GiteeAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder, httpClientFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public override string AuthenticationSchemeNmae => GiteeAuthenticationDefaults.AuthenticationScheme;
|
||||
|
||||
protected override async Task<List<Claim>> GetAuthTicketAsync(string code)
|
||||
{
|
||||
//获取 accessToken
|
||||
var tokenQueryKv = new List<KeyValuePair<string, string?>>()
|
||||
{
|
||||
new KeyValuePair<string, string?>("grant_type","authorization_code"),
|
||||
new KeyValuePair<string, string?>("client_id",Options.ClientId),
|
||||
new KeyValuePair<string, string?>("client_secret",Options.ClientSecret),
|
||||
new KeyValuePair<string, string?>("redirect_uri",Options.RedirectUri),
|
||||
new KeyValuePair<string, string?>("code",code)
|
||||
};
|
||||
var tokenModel = await SendHttpRequestAsync<GiteeAuthticationcationTokenResponse>(GiteeAuthenticationDefaults.TokenEndpoint, tokenQueryKv,HttpMethod.Post);
|
||||
|
||||
//获取 userInfo
|
||||
var userInfoQueryKv = new List<KeyValuePair<string, string?>>()
|
||||
{
|
||||
new KeyValuePair<string, string?>("access_token",tokenModel.access_token),
|
||||
};
|
||||
var userInfoMdoel = await SendHttpRequestAsync<GiteeAuthticationcationUserInfoResponse>(GiteeAuthenticationDefaults.UserInformationEndpoint, userInfoQueryKv);
|
||||
|
||||
List<Claim> claims = new List<Claim>()
|
||||
{
|
||||
new Claim(Claims.AvatarUrl, userInfoMdoel.avatar_url),
|
||||
new Claim(Claims.Url, userInfoMdoel.url),
|
||||
|
||||
new Claim(Claims.OpenId,userInfoMdoel.id.ToString()),
|
||||
new Claim(Claims.Name, userInfoMdoel.name),
|
||||
new Claim(Claims.AccessToken, tokenModel.access_token)
|
||||
};
|
||||
return claims;
|
||||
}
|
||||
|
||||
protected override void VerifyErrResponse(string content)
|
||||
{
|
||||
GiteeAuthticationErrCodeModel.VerifyErrResponse(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
using static Yi.Framework.AspNetCore.Authentication.OAuth.Gitee.GiteeAuthenticationConstants;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a set of options used by <see cref="GiteeAuthenticationHandler"/>.
|
||||
/// </summary>
|
||||
public class GiteeAuthenticationOptions : OAuthOptions
|
||||
{
|
||||
public GiteeAuthenticationOptions()
|
||||
{
|
||||
ClaimsIssuer = GiteeAuthenticationDefaults.Issuer;
|
||||
|
||||
CallbackPath = GiteeAuthenticationDefaults.CallbackPath;
|
||||
|
||||
AuthorizationEndpoint = GiteeAuthenticationDefaults.AuthorizationEndpoint;
|
||||
TokenEndpoint = GiteeAuthenticationDefaults.TokenEndpoint;
|
||||
UserInformationEndpoint = GiteeAuthenticationDefaults.UserInformationEndpoint;
|
||||
UserEmailsEndpoint = GiteeAuthenticationDefaults.UserEmailsEndpoint;
|
||||
|
||||
Scope.Add("user_info");
|
||||
Scope.Add("emails");
|
||||
|
||||
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
|
||||
ClaimActions.MapJsonKey(ClaimTypes.Name, "login");
|
||||
ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
|
||||
ClaimActions.MapJsonKey(Claims.Name, "name");
|
||||
ClaimActions.MapJsonKey(Claims.Url, "url");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the address of the endpoint exposing
|
||||
/// the email addresses associated with the logged in user.
|
||||
/// </summary>
|
||||
public string UserEmailsEndpoint { get; set; }
|
||||
|
||||
public string RedirectUri { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee
|
||||
{
|
||||
public class GiteeAuthticationErrCodeModel
|
||||
{
|
||||
public string error { get; set; }
|
||||
|
||||
public string error_description { get; set; }
|
||||
|
||||
public static void VerifyErrResponse(string content)
|
||||
{
|
||||
|
||||
var model =Newtonsoft.Json.JsonConvert.DeserializeObject <GiteeAuthticationErrCodeModel>(content);
|
||||
if (model.error != null)
|
||||
{
|
||||
|
||||
throw new Exception($"第三方授权返回错误,错误码:【{model.error}】,错误详情:【{model.error_description}】");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.Gitee
|
||||
{
|
||||
public class GiteeAuthticationcationTokenResponse
|
||||
{
|
||||
public string access_token { get; set; }
|
||||
public string token_type { get; set; }
|
||||
public int expires_in { get; set; }
|
||||
public string refresh_token { get; set; }
|
||||
public string scope { get; set; }
|
||||
public long created_at { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class GiteeAuthticationcationOpenIdResponse
|
||||
{
|
||||
public string client_id { get; set; }
|
||||
|
||||
public string openid { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class GiteeAuthticationcationUserInfoResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 也可以等于openId
|
||||
/// </summary>
|
||||
public int id { get; set; }
|
||||
public string login { get; set; }
|
||||
public string name { get; set; }
|
||||
public string avatar_url { get; set; }
|
||||
public string url { get; set; }
|
||||
public string html_url { get; set; }
|
||||
public string remark { get; set; }
|
||||
public string followers_url { get; set; }
|
||||
public string following_url { get; set; }
|
||||
public string gists_url { get; set; }
|
||||
public string starred_url { get; set; }
|
||||
public string subscriptions_url { get; set; }
|
||||
public string organizations_url { get; set; }
|
||||
public string repos_url { get; set; }
|
||||
public string events_url { get; set; }
|
||||
public string received_events_url { get; set; }
|
||||
public string type { get; set; }
|
||||
public string blog { get; set; }
|
||||
public string weibo { get; set; }
|
||||
public string bio { get; set; }
|
||||
public int public_repos { get; set; }
|
||||
public int public_gists { get; set; }
|
||||
public int followers { get; set; }
|
||||
public int following { get; set; }
|
||||
public int stared { get; set; }
|
||||
public int watched { get; set; }
|
||||
public DateTime created_at { get; set; }
|
||||
public DateTime updated_at { get; set; }
|
||||
public string email { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth
|
||||
{
|
||||
public abstract class OauthAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : AuthenticationSchemeOptions, new()
|
||||
{
|
||||
public abstract string AuthenticationSchemeNmae { get; }
|
||||
private AuthenticationScheme _scheme;
|
||||
|
||||
public OauthAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder)
|
||||
{
|
||||
HttpClientFactory = httpClientFactory;
|
||||
HttpClient = HttpClientFactory.CreateClient();
|
||||
}
|
||||
|
||||
|
||||
protected IHttpClientFactory HttpClientFactory { get; }
|
||||
|
||||
protected HttpClient HttpClient { get; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成认证票据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private AuthenticationTicket TicketConver(List<Claim> claims)
|
||||
{
|
||||
var claimsIdentity = new ClaimsIdentity(claims.ToArray(), AuthenticationSchemeNmae);
|
||||
var principal = new ClaimsPrincipal(claimsIdentity);
|
||||
return new AuthenticationTicket(principal, AuthenticationSchemeNmae);
|
||||
}
|
||||
|
||||
protected async Task<HttpModel> SendHttpRequestAsync<HttpModel>(string url, IEnumerable<KeyValuePair<string, string?>> query, HttpMethod? httpMethod = null)
|
||||
{
|
||||
httpMethod = httpMethod ?? HttpMethod.Get;
|
||||
|
||||
var queryUrl = QueryHelpers.AddQueryString(url, query);
|
||||
HttpResponseMessage response = null;
|
||||
if (httpMethod == HttpMethod.Get)
|
||||
{
|
||||
response = await HttpClient.GetAsync(queryUrl);
|
||||
}
|
||||
else if (httpMethod == HttpMethod.Post)
|
||||
{
|
||||
response = await HttpClient.PostAsync(queryUrl, null);
|
||||
}
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception($"授权服务器请求错误,请求地址:{queryUrl},错误信息:{content}");
|
||||
}
|
||||
VerifyErrResponse(content);
|
||||
var model = Newtonsoft.Json.JsonConvert.DeserializeObject<HttpModel>(content);
|
||||
return model!;
|
||||
}
|
||||
|
||||
protected virtual void VerifyErrResponse(string content)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
protected abstract Task<List<Claim>> GetAuthTicketAsync(string code);
|
||||
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!Context.Request.Query.ContainsKey("code"))
|
||||
{
|
||||
return AuthenticateResult.Fail("回调未包含code参数");
|
||||
}
|
||||
var code = Context.Request.Query["code"].ToString();
|
||||
|
||||
List<Claim> authTicket = null;
|
||||
try
|
||||
{
|
||||
authTicket = await GetAuthTicketAsync(code);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return AuthenticateResult.Fail(ex.Message ?? "未知错误");
|
||||
}
|
||||
//成功
|
||||
var result = AuthenticateResult.Success(TicketConver(authTicket));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
|
||||
|
||||
/// <summary>
|
||||
/// Contains constants specific to the <see cref="QQAuthenticationHandler"/>.
|
||||
/// </summary>
|
||||
public static class QQAuthenticationConstants
|
||||
{
|
||||
public static class Claims
|
||||
{
|
||||
public const string AvatarFullUrl = "urn:qq:avatar_full";
|
||||
public const string AvatarUrl = "urn:qq:avatar";
|
||||
public const string PictureFullUrl = "urn:qq:picture_full";
|
||||
public const string PictureMediumUrl = "urn:qq:picture_medium";
|
||||
public const string PictureUrl = "urn:qq:picture";
|
||||
public const string UnionId = "urn:qq:unionid";
|
||||
|
||||
public const string OpenId = "urn:openid";
|
||||
public const string AccessToken = "urn:access_token";
|
||||
public const string Name = "urn:name";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
|
||||
|
||||
/// <summary>
|
||||
/// Default values for QQ authentication.
|
||||
/// </summary>
|
||||
public static class QQAuthenticationDefaults
|
||||
{
|
||||
/// <summary>
|
||||
/// Default value for <see cref="AuthenticationScheme.Name"/>.
|
||||
/// </summary>
|
||||
public const string AuthenticationScheme = "QQ";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
|
||||
/// </summary>
|
||||
public static readonly string DisplayName = "QQ";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
|
||||
/// </summary>
|
||||
public static readonly string Issuer = "QQ";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
|
||||
/// </summary>
|
||||
public static readonly string CallbackPath = "/signin-qq";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string AuthorizationEndpoint = "https://graph.qq.com/oauth2.0/authorize";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string TokenEndpoint = "https://graph.qq.com/oauth2.0/token";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="QQAuthenticationOptions.UserIdentificationEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string UserIdentificationEndpoint = "https://graph.qq.com/oauth2.0/me";
|
||||
|
||||
/// <summary>
|
||||
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
|
||||
/// </summary>
|
||||
public static readonly string UserInformationEndpoint = "https://graph.qq.com/user/get_user_info";
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods to add QQ authentication capabilities to an HTTP application pipeline.
|
||||
/// </summary>
|
||||
public static class QQAuthenticationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddQQ([NotNull] this AuthenticationBuilder builder)
|
||||
{
|
||||
return builder.AddQQ(QQAuthenticationDefaults.AuthenticationScheme, options => { });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddQQ(
|
||||
[NotNull] this AuthenticationBuilder builder,
|
||||
[NotNull] Action<QQAuthenticationOptions> configuration)
|
||||
{
|
||||
return builder.AddQQ(QQAuthenticationDefaults.AuthenticationScheme, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <param name="scheme">The authentication scheme associated with this instance.</param>
|
||||
/// <param name="configuration">The delegate used to configure the QQ options.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddQQ(
|
||||
[NotNull] this AuthenticationBuilder builder,
|
||||
[NotNull] string scheme,
|
||||
[NotNull] Action<QQAuthenticationOptions> configuration)
|
||||
{
|
||||
return builder.AddQQ(scheme, QQAuthenticationDefaults.DisplayName, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds <see cref="QQAuthenticationHandler"/> to the specified
|
||||
/// <see cref="AuthenticationBuilder"/>, which enables QQ authentication capabilities.
|
||||
/// </summary>
|
||||
/// <param name="builder">The authentication builder.</param>
|
||||
/// <param name="scheme">The authentication scheme associated with this instance.</param>
|
||||
/// <param name="caption">The optional display name associated with this instance.</param>
|
||||
/// <param name="configuration">The delegate used to configure the QQ options.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddQQ(
|
||||
[NotNull] this AuthenticationBuilder builder,
|
||||
[NotNull] string scheme,
|
||||
[CanBeNull] string caption,
|
||||
[NotNull] Action<QQAuthenticationOptions> configuration)
|
||||
{
|
||||
return builder.AddScheme<QQAuthenticationOptions, QQAuthenticationHandler>(scheme, caption, configuration);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using static Yi.Framework.AspNetCore.Authentication.OAuth.QQ.QQAuthenticationConstants;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ
|
||||
{
|
||||
public class QQAuthenticationHandler : OauthAuthenticationHandler<QQAuthenticationOptions>
|
||||
{
|
||||
public QQAuthenticationHandler(IOptionsMonitor<QQAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, IHttpClientFactory httpClientFactory) : base(options, logger, encoder, httpClientFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public override string AuthenticationSchemeNmae => QQAuthenticationDefaults.AuthenticationScheme;
|
||||
|
||||
protected override void VerifyErrResponse(string content)
|
||||
{
|
||||
QQAuthticationErrCodeModel.VerifyErrResponse(content);
|
||||
}
|
||||
|
||||
protected override async Task<List<Claim>> GetAuthTicketAsync(string code)
|
||||
{
|
||||
|
||||
//获取 accessToken
|
||||
var tokenQueryKv = new List<KeyValuePair<string, string?>>()
|
||||
{
|
||||
new KeyValuePair<string, string?>("grant_type","authorization_code"),
|
||||
new KeyValuePair<string, string?>("client_id",Options.ClientId),
|
||||
new KeyValuePair<string, string?>("client_secret",Options.ClientSecret),
|
||||
new KeyValuePair<string, string?>("redirect_uri",Options.RedirectUri),
|
||||
new KeyValuePair<string, string?>("fmt","json"),
|
||||
new KeyValuePair<string, string?>("need_openid","1"),
|
||||
new KeyValuePair<string, string?>("code",code),
|
||||
new KeyValuePair<string, string?>("state","true"),
|
||||
};
|
||||
var tokenModel = await SendHttpRequestAsync<QQAuthticationcationTokenResponse>(QQAuthenticationDefaults.TokenEndpoint, tokenQueryKv);
|
||||
|
||||
|
||||
|
||||
//获取 userInfo
|
||||
var userInfoQueryKv = new List<KeyValuePair<string, string?>>()
|
||||
{
|
||||
new KeyValuePair<string, string?>("access_token",tokenModel.access_token),
|
||||
new KeyValuePair<string, string?>("oauth_consumer_key",Options.ClientId),
|
||||
new KeyValuePair<string, string?>("openid",tokenModel.openid),
|
||||
};
|
||||
|
||||
var userInfoMdoel = await SendHttpRequestAsync<QQAuthticationcationUserInfoResponse>(QQAuthenticationDefaults.UserInformationEndpoint, userInfoQueryKv);
|
||||
|
||||
|
||||
List<Claim> claims = new List<Claim>()
|
||||
{
|
||||
|
||||
new Claim(Claims.AvatarFullUrl, userInfoMdoel.figureurl_qq_2),
|
||||
new Claim(Claims.AvatarUrl, userInfoMdoel.figureurl_qq_1),
|
||||
new Claim(Claims.PictureFullUrl, userInfoMdoel.figureurl_2),
|
||||
new Claim(Claims.PictureMediumUrl, userInfoMdoel.figureurl_qq_1),
|
||||
new Claim(Claims.PictureUrl, userInfoMdoel.figureurl),
|
||||
|
||||
new Claim(Claims.OpenId, tokenModel.openid),
|
||||
new Claim(Claims.Name, userInfoMdoel.nickname),
|
||||
new Claim(Claims.AccessToken, tokenModel.access_token),
|
||||
|
||||
};
|
||||
return claims;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
|
||||
* for more information concerning the license and the contributors participating to this project.
|
||||
*/
|
||||
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
using static Yi.Framework.AspNetCore.Authentication.OAuth.QQ.QQAuthenticationConstants;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a set of options used by <see cref="QQAuthenticationHandler"/>.
|
||||
/// </summary>
|
||||
public class QQAuthenticationOptions : OAuthOptions
|
||||
{
|
||||
public QQAuthenticationOptions()
|
||||
{
|
||||
ClaimsIssuer = QQAuthenticationDefaults.Issuer;
|
||||
CallbackPath = QQAuthenticationDefaults.CallbackPath;
|
||||
|
||||
AuthorizationEndpoint = QQAuthenticationDefaults.AuthorizationEndpoint;
|
||||
TokenEndpoint = QQAuthenticationDefaults.TokenEndpoint;
|
||||
UserIdentificationEndpoint = QQAuthenticationDefaults.UserIdentificationEndpoint;
|
||||
UserInformationEndpoint = QQAuthenticationDefaults.UserInformationEndpoint;
|
||||
|
||||
Scope.Add("get_user_info");
|
||||
|
||||
ClaimActions.MapJsonKey(ClaimTypes.Name, "nickname");
|
||||
ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender");
|
||||
ClaimActions.MapJsonKey(Claims.PictureUrl, "figureurl");
|
||||
ClaimActions.MapJsonKey(Claims.PictureMediumUrl, "figureurl_1");
|
||||
ClaimActions.MapJsonKey(Claims.PictureFullUrl, "figureurl_2");
|
||||
ClaimActions.MapJsonKey(Claims.AvatarUrl, "figureurl_qq_1");
|
||||
ClaimActions.MapJsonKey(Claims.AvatarFullUrl, "figureurl_qq_2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the union Id (the primary key of an owner for different apps of the QQ platform) should be put into the user claims.
|
||||
/// </summary>
|
||||
public bool ApplyForUnionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URL of the user identification endpoint (a.k.a. the "OpenID endpoint").
|
||||
/// </summary>
|
||||
public string UserIdentificationEndpoint { get; set; }
|
||||
|
||||
public string RedirectUri { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ
|
||||
{
|
||||
public class QQAuthticationErrCodeModel
|
||||
{
|
||||
public string error { get; set; }
|
||||
|
||||
public string error_description { get; set; }
|
||||
|
||||
public static void VerifyErrResponse(string content)
|
||||
{
|
||||
|
||||
var model =Newtonsoft.Json.JsonConvert.DeserializeObject <QQAuthticationErrCodeModel>(content);
|
||||
if (model.error != null)
|
||||
{
|
||||
|
||||
throw new Exception($"第三方授权返回错误,错误码:【{model.error}】,错误详情:【{model.error_description}】");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth.QQ
|
||||
{
|
||||
public class QQAuthticationcationTokenResponse
|
||||
{
|
||||
public string access_token { get; set; }
|
||||
|
||||
public string expires_in { get; set; }
|
||||
|
||||
public string refresh_token { get; set; }
|
||||
|
||||
public string openid { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class QQAuthticationcationOpenIdResponse
|
||||
{
|
||||
public string client_id { get; set; }
|
||||
|
||||
public string openid { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class QQAuthticationcationUserInfoResponse
|
||||
{
|
||||
// 返回码
|
||||
public int ret { get; set; }
|
||||
|
||||
// 如果ret<0,会有相应的错误信息提示
|
||||
// 返回数据全部用UTF-8编码
|
||||
public string msg { get; set; }
|
||||
|
||||
// 判断是否有数据丢失
|
||||
// 0或者不返回:没有数据丢失,可以缓存
|
||||
// 1:有部分数据丢失或错误,不要缓存
|
||||
public int is_lost { get; set; }
|
||||
|
||||
// 用户在QQ空间的昵称
|
||||
public string nickname { get; set; }
|
||||
|
||||
// 大小为30x30像素的QQ空间头像URL
|
||||
public string figureurl { get; set; }
|
||||
|
||||
// 大小为50x50像素的QQ空间头像URL
|
||||
public string figureurl_1 { get; set; }
|
||||
|
||||
// 大小为100x100像素的QQ空间头像URL
|
||||
public string figureurl_2 { get; set; }
|
||||
|
||||
// 大小为40x40像素的QQ头像URL
|
||||
public string figureurl_qq_1 { get; set; }
|
||||
|
||||
// 大小为100x100像素的QQ头像URL
|
||||
// 需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有
|
||||
public string figureurl_qq_2 { get; set; }
|
||||
|
||||
// 性别。如果获取不到则默认返回"男"
|
||||
public string gender { get; set; }
|
||||
|
||||
// 性别类型。默认返回2
|
||||
public int gender_type { get; set; }
|
||||
|
||||
// 省
|
||||
public string province { get; set; }
|
||||
|
||||
// 市
|
||||
public string city { get; set; }
|
||||
|
||||
// 年
|
||||
public int year { get; set; }
|
||||
|
||||
// 星座
|
||||
public string constellation { get; set; }
|
||||
|
||||
// 标识用户是否为黄钻用户
|
||||
public int is_yellow_vip { get; set; }
|
||||
|
||||
// 黄钻等级
|
||||
public int yellow_vip_level { get; set; }
|
||||
|
||||
// 是否为年费黄钻用户
|
||||
public int is_yellow_year_vip { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yi.Framework.AspNetCore\Yi.Framework.AspNetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Volo.Abp.Modularity;
|
||||
using Yi.Framework.Core;
|
||||
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Authentication.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// 本模块轮子来自 AspNet.Security.OAuth.QQ;
|
||||
/// </summary>
|
||||
[DependsOn(typeof(YiFrameworkAspNetCoreModule))]
|
||||
public class YiFrameworkAspNetCoreAuthenticationOAuthModule:AbpModule
|
||||
{
|
||||
public override void ConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
var service = context.Services;
|
||||
service.AddHttpClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user