Просмотр исходного кода

😁增加第三方授权登录Weixin、Gitee

zuohuaijun 2 лет назад
Родитель
Сommit
65e67f39ab

+ 6 - 2
Admin.NET/Admin.NET.Application/Configuration/OAuth.json

@@ -3,8 +3,12 @@
 
   "OAuth": {
     "Weixin": {
-      "ClientId": "wxd77174ddc828b65b",
-      "ClientSecret": "6224502b24d31acf8b4e0dc8e482317c"
+      "ClientId": "wx2959fdd3abc05362",
+      "ClientSecret": "829f65b2be0652bcd50ea8cb820fd7fa"
+    },
+    "Gitee": {
+      "ClientId": "daf46f82c7aef4ee192e7537319f7fd18a158b7f0b21ca11abccea59c55d8ac1",
+      "ClientSecret": "abf5d832a02503eb7e609bed09bcdd3f23bfc9461009f0602acf9a59f789fcd5"
     }
   }
 }

+ 4 - 2
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -17,6 +17,8 @@
   <ItemGroup>
     <PackageReference Include="AngleSharp" Version="1.0.4" />
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
+    <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="6.0.15" />
+    <PackageReference Include="AspNet.Security.OAuth.Weixin" Version="6.0.15" />
     <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
     <PackageReference Include="FluentEmail.Smtp" Version="3.0.2" />
     <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.8.41" />
@@ -30,8 +32,8 @@
     <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.21" />
     <PackageReference Include="NEST" Version="7.17.5" />
     <PackageReference Include="NewLife.Redis" Version="5.5.2023.803" />
-    <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.2.0" />
-    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="2.30.0" />
+    <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.1.9" />
+    <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="2.31.0" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="2.20.0" />
     <PackageReference Include="SqlSugarCore" Version="5.1.4.102" />
     <PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.4" />

+ 6 - 1
Admin.NET/Admin.NET.Core/Option/OAuthOptions.cs

@@ -15,7 +15,12 @@ namespace Admin.NET.Core;
 public sealed class OAuthOptions : IConfigurableOptions
 {
     /// <summary>
-    /// 微信配置
+    /// Weixin配置
     /// </summary>
     public Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions Weixin { get; set; }
+
+    /// <summary>
+    /// Gitee配置
+    /// </summary>
+    public Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions Gitee { get; set; }
 }

+ 35 - 0
Admin.NET/Admin.NET.Core/Service/OAuth/HttpContextExtension.cs

@@ -0,0 +1,35 @@
+// 麻省理工学院许可证
+//
+// 版权所有 (c) 2021-2023 zuohuaijun,大名科技(天津)有限公司  联系电话/微信:18020030720  QQ:515096995
+//
+// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
+//
+// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
+// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
+
+using Microsoft.AspNetCore.Authentication;
+
+namespace Admin.NET.Core.Service;
+
+public static class HttpContextExtension
+{
+    public static async Task<AuthenticationScheme[]> GetExternalProvidersAsync(this HttpContext context)
+    {
+        ArgumentNullException.ThrowIfNull(context);
+
+        var schemes = context.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
+
+        return (from scheme in await schemes.GetAllSchemesAsync()
+                where !string.IsNullOrEmpty(scheme.DisplayName)
+                select scheme).ToArray();
+    }
+
+    public static async Task<bool> IsProviderSupportedAsync(this HttpContext context, string provider)
+    {
+        ArgumentNullException.ThrowIfNull(context);
+
+        return (from scheme in await context.GetExternalProvidersAsync()
+                where string.Equals(scheme.Name, provider, StringComparison.OrdinalIgnoreCase)
+                select scheme).Any();
+    }
+}

+ 16 - 0
Admin.NET/Admin.NET.Core/Service/OAuth/OAuthClaim.cs

@@ -0,0 +1,16 @@
+// 麻省理工学院许可证
+//
+// 版权所有 (c) 2021-2023 zuohuaijun,大名科技(天津)有限公司  联系电话/微信:18020030720  QQ:515096995
+//
+// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
+//
+// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
+// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
+
+namespace Admin.NET.Core.Service;
+
+public static class GiteeClaims
+{
+    public const string Name = "urn:gitee:name";
+    public const string AvatarUrl = "urn:gitee:avatar_url";
+}

+ 116 - 0
Admin.NET/Admin.NET.Core/Service/OAuth/SysOAuthService.cs

@@ -0,0 +1,116 @@
+// 麻省理工学院许可证
+//
+// 版权所有 (c) 2021-2023 zuohuaijun,大名科技(天津)有限公司  联系电话/微信:18020030720  QQ:515096995
+//
+// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
+//
+// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
+// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
+
+using Microsoft.AspNetCore.Authentication;
+using System.Security.Claims;
+
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统OAuth服务
+/// </summary>
+[AllowAnonymous]
+[ApiDescriptionSettings(Order = 100)]
+public class SysOAuthService : IDynamicApiController, ITransient
+{
+    private readonly IHttpContextAccessor _httpContextAccessor;
+    private readonly SqlSugarRepository<SysWechatUser> _sysWechatUserRep;
+    private readonly SysUserService _sysUserService;
+
+    public SysOAuthService(IHttpContextAccessor httpContextAccessor,
+        SqlSugarRepository<SysWechatUser> sysWechatUserRep,
+        SysUserService sysUserService)
+    {
+        _httpContextAccessor = httpContextAccessor;
+        _sysWechatUserRep = sysWechatUserRep;
+        _sysUserService = sysUserService;
+    }
+
+    /// <summary>
+    /// 第三方登录
+    /// </summary>
+    /// <param name="provider"></param>
+    /// <param name="redirectUrl"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "SignIn"), HttpGet]
+    [DisplayName("第三方登录")]
+    public async Task<IActionResult> SignIn([FromQuery] string provider, [FromQuery] string redirectUrl)
+    {
+        if (string.IsNullOrWhiteSpace(provider) || !await _httpContextAccessor.HttpContext.IsProviderSupportedAsync(provider))
+            throw Oops.Oh("不支持的OAuth类型");
+
+        var request = _httpContextAccessor.HttpContext.Request;
+        var url = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}Callback?provider={provider}&redirectUrl={redirectUrl}";
+        var properties = new AuthenticationProperties { RedirectUri = url };
+        properties.Items["LoginProvider"] = provider;
+        return await Task.FromResult(new ChallengeResult(provider, properties));
+    }
+
+    /// <summary>
+    /// 授权回调
+    /// </summary>
+    /// <param name="provider"></param>
+    /// <param name="redirectUrl"></param>
+    /// <returns></returns>
+    [ApiDescriptionSettings(Name = "SignInCallback"), HttpGet]
+    [DisplayName("授权回调")]
+    public async Task<IActionResult> SignInCallback([FromQuery] string provider = null, [FromQuery] string redirectUrl = "")
+    {
+        if (string.IsNullOrWhiteSpace(provider) || !await _httpContextAccessor.HttpContext.IsProviderSupportedAsync(provider))
+            throw Oops.Oh("不支持的OAuth类型");
+
+        var authenticateResult = await _httpContextAccessor.HttpContext.AuthenticateAsync(provider);
+        if (!authenticateResult.Succeeded)
+            throw Oops.Oh("授权失败");
+
+        var openIdClaim = authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier);
+        if (openIdClaim == null || string.IsNullOrWhiteSpace(openIdClaim.Value))
+            throw Oops.Oh("授权失败");
+
+        if (provider == "Weixin")
+        {
+        }
+        else if (provider == "Gitee")
+        {
+            string email = authenticateResult.Principal.FindFirst(ClaimTypes.Email)?.Value;
+            string name = authenticateResult.Principal.FindFirst(ClaimTypes.Name)?.Value;
+            string giteeName = authenticateResult.Principal.FindFirst(GiteeClaims.Name)?.Value;
+            string avatarUrl = authenticateResult.Principal.FindFirst(GiteeClaims.AvatarUrl)?.Value;
+
+            // 若账号不存在则新建
+            var user = await _sysWechatUserRep.GetFirstAsync(u => u.OpenId == openIdClaim.Value);
+            if (user == null)
+            {
+                var userId = await _sysUserService.AddUser(new AddUserInput()
+                {
+                    Account = name,
+                    RealName = name,
+                    NickName = name,
+                    Email = email,
+                    Avatar = avatarUrl,
+                    Phone = "",
+                });
+
+                user = await _sysWechatUserRep.InsertReturnEntityAsync(new SysWechatUser()
+                {
+                    UserId = userId,
+                    OpenId = openIdClaim.Value,
+                    Avatar = avatarUrl,
+                    NickName = name,
+                });
+            }
+
+            // 构建登录Token
+
+
+        }
+
+        return new RedirectResult($"{redirectUrl}?openId={openIdClaim.Value}");
+    }
+}

+ 3 - 1
Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs

@@ -76,7 +76,7 @@ public class SysUserService : IDynamicApiController, ITransient
     [UnitOfWork]
     [ApiDescriptionSettings(Name = "Add"), HttpPost]
     [DisplayName("增加用户")]
-    public async Task AddUser(AddUserInput input)
+    public async Task<long> AddUser(AddUserInput input)
     {
         var isExist = await _sysUserRep.AsQueryable().Filter(null, true).AnyAsync(u => u.Account == input.Account);
         if (isExist) throw Oops.Oh(ErrorCodeEnum.D1003);
@@ -88,6 +88,8 @@ public class SysUserService : IDynamicApiController, ITransient
         var newUser = await _sysUserRep.AsInsertable(user).ExecuteReturnEntityAsync();
         input.Id = newUser.Id;
         await UpdateRoleAndExtOrg(input);
+
+        return newUser.Id;
     }
 
     /// <summary>

+ 23 - 8
Admin.NET/Admin.NET.Web.Core/Startup.cs

@@ -14,6 +14,9 @@ using Furion;
 using Furion.SpecificationDocument;
 using Furion.VirtualFileServer;
 using IGeekFan.AspNetCore.Knife4jUI;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.HttpOverrides;
@@ -82,14 +85,26 @@ public class Startup : AppStartup
             //.AddXmlDataContractSerializerFormatters()
             .AddInjectWithUnifyResult<AdminResultProvider>();
 
-        //// 第三方授权登录
-        //services.AddAuthentication()
-        //    .AddWeixin(options =>
-        //    {
-        //        var opt = App.GetOptions<OAuthOptions>();
-        //        options.ClientId = opt.Weixin.ClientId;
-        //        options.ClientSecret = opt.Weixin.ClientSecret;
-        //    });
+        // 第三方授权登录
+        var authOpt = App.GetOptions<OAuthOptions>();
+        services.AddAuthentication(options =>
+            {
+                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
+                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+            })
+            .AddCookie()
+            .AddWeixin(options =>
+            {
+                options.ClientId = authOpt.Weixin.ClientId;
+                options.ClientSecret = authOpt.Weixin.ClientSecret;
+            })
+            .AddGitee(options =>
+            {
+                options.ClientId = authOpt.Gitee.ClientId;
+                options.ClientSecret = authOpt.Gitee.ClientSecret;
+                options.ClaimActions.MapJsonKey(GiteeClaims.Name, "name");
+                options.ClaimActions.MapJsonKey(GiteeClaims.AvatarUrl, "avatar_url");
+            });
 
         // ElasticSearch
         services.AddElasticSearch();

+ 27 - 27
Admin.NET/Admin.NET.Web.Entry/Properties/launchSettings.json

@@ -1,32 +1,32 @@
 {
-    "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "http://json.schemastore.org/launchsettings.json",
 
-    "iisSettings": {
-        "windowsAuthentication": false,
-        "anonymousAuthentication": true,
-        "iisExpress": {
-            "applicationUrl": "http://localhost:5005"
-            //"sslPort": 44325
-        }
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:5005"
+      // "sslPort": 44325
+    }
+  },
+  "profiles": {
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "launchUrl": "",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
     },
-    "profiles": {
-        "IIS Express": {
-            "commandName": "IISExpress",
-            "launchBrowser": true,
-            "launchUrl": "",
-            "environmentVariables": {
-                "ASPNETCORE_ENVIRONMENT": "Development"
-            }
-        },
-        "Admin.NET.Web.Entry": {
-            "commandName": "Project",
-            "dotnetRunMessages": true,
-            "launchBrowser": true,
-            "launchUrl": "",
-            "applicationUrl": "http://localhost:5005",
-            "environmentVariables": {
-                "ASPNETCORE_ENVIRONMENT": "Development"
-            }
-        }
+    "Admin.NET.Web.Entry": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
+      "launchBrowser": true,
+      "launchUrl": "",
+      "applicationUrl": "http://localhost:5005",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
     }
+  }
 }