mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-03-18 15:36:37 +08:00
feat: 新增Oauth鉴权模块,支持qq登录、gitee登录
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user