committed by
GitHub
10 changed files with 322 additions and 11 deletions
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.Account; |
|||
public class ExternalLoginInfoDto |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string DisplayName { get; set; } |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Account; |
|||
public class ExternalLoginResultDto |
|||
{ |
|||
public List<UserLoginInfoDto> UserLogins { get; set; } = new List<UserLoginInfoDto>(); |
|||
public List<ExternalLoginInfoDto> ExternalLogins { get; set; } = new List<ExternalLoginInfoDto>(); |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Abp.Account; |
|||
public class RemoveExternalLoginInput |
|||
{ |
|||
[Required] |
|||
public string LoginProvider { get; set; } |
|||
|
|||
[Required] |
|||
public string ProviderKey { get; set; } |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.Account; |
|||
public class UserLoginInfoDto |
|||
{ |
|||
public string LoginProvider { get; set; } |
|||
public string ProviderKey { get; set; } |
|||
public string ProviderDisplayName { get; set; } |
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
using LINGYUN.Abp.Account.Web.ExternalProviders; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.WebUtilities; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Account; |
|||
using Volo.Abp.Account.Localization; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.Identity.AspNetCore; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Abp.Account.Web.Areas.Account.Controllers; |
|||
|
|||
[Controller] |
|||
[Area(AccountRemoteServiceConsts.ModuleName)] |
|||
[Route($"api/{AccountRemoteServiceConsts.ModuleName}")] |
|||
[RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)] |
|||
public class AccountController : AbpController |
|||
{ |
|||
protected AbpSignInManager SignInManager => LazyServiceProvider.LazyGetRequiredService<AbpSignInManager>(); |
|||
protected IdentityUserManager UserManager => LazyServiceProvider.LazyGetRequiredService<IdentityUserManager>(); |
|||
protected IExternalProviderService ExternalProviderService => LazyServiceProvider.LazyGetRequiredService<IExternalProviderService>(); |
|||
|
|||
public AccountController() |
|||
{ |
|||
LocalizationResource = typeof(AccountResource); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Authorize] |
|||
[Route("external-logins")] |
|||
public async virtual Task<ExternalLoginResultDto> GetExternalLoginsAsync() |
|||
{ |
|||
var currentUser = await UserManager.GetByIdAsync(CurrentUser.GetId()); |
|||
var userLogins = await UserManager.GetLoginsAsync(currentUser); |
|||
var externalProviders = await ExternalProviderService.GetAllAsync(); |
|||
|
|||
return new ExternalLoginResultDto |
|||
{ |
|||
UserLogins = userLogins.Select(x => new UserLoginInfoDto |
|||
{ |
|||
ProviderDisplayName = x.ProviderDisplayName, |
|||
ProviderKey = x.ProviderKey, |
|||
LoginProvider = x.LoginProvider, |
|||
}).ToList(), |
|||
ExternalLogins = externalProviders.Select(x => |
|||
{ |
|||
return new ExternalLoginInfoDto |
|||
{ |
|||
Name = x.Name, |
|||
DisplayName = x.DisplayName, |
|||
}; |
|||
}).ToList(), |
|||
}; |
|||
} |
|||
|
|||
[HttpDelete] |
|||
[Authorize] |
|||
[Route("external-logins/remove")] |
|||
public async virtual Task RemoveExternalLoginAsync(RemoveExternalLoginInput input) |
|||
{ |
|||
var currentUser = await UserManager.GetByIdAsync(CurrentUser.GetId()); |
|||
var identityResult = await UserManager.RemoveLoginAsync( |
|||
currentUser, |
|||
input.LoginProvider, |
|||
input.ProviderKey); |
|||
|
|||
if (!identityResult.Succeeded) |
|||
{ |
|||
throw new UserFriendlyException("Operation failed: " + identityResult.Errors.Select(e => $"[{e.Code}] {e.Description}").JoinAsString(", ")); |
|||
} |
|||
// 解绑的是当前身份认证方案则退出登录
|
|||
var amr = CurrentUser.FindClaimValue(ClaimTypes.AuthenticationMethod); |
|||
if (!amr.IsNullOrWhiteSpace() && string.Equals(amr, input.LoginProvider, StringComparison.InvariantCultureIgnoreCase)) |
|||
{ |
|||
await SignInManager.SignOutAsync(); |
|||
} |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Authorize] |
|||
[Route("external-logins/bind")] |
|||
public virtual async Task<IActionResult> ExternalLoginBindAsync(string provider, string returnUrl) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(returnUrl)) |
|||
{ |
|||
Logger.LogWarning("The parameter is incorrect"); |
|||
return Redirect(QueryHelpers.AddQueryString(returnUrl, new Dictionary<string, string>() |
|||
{ |
|||
["error"] = "The parameter is incorrect" |
|||
})); |
|||
} |
|||
|
|||
var tenantId = CurrentTenant.Id; |
|||
var userId = CurrentUser.GetId(); |
|||
|
|||
var redirectUrl = Url.Page("/Account/ExternalLoginBind", pageHandler: "BindCallback", values: new { returnUrl, userId, tenantId }); |
|||
|
|||
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, userId.ToString()); |
|||
properties.Items["scheme"] = provider; |
|||
|
|||
return await Task.FromResult(Challenge(properties, provider)); |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
@page |
|||
@model LINGYUN.Abp.Account.Web.Pages.Account.ExternalLoginBindModel |
|||
@{ |
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.WebUtilities; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.Identity.AspNetCore; |
|||
using Volo.Abp.Uow; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Abp.Account.Web.Pages.Account; |
|||
|
|||
[IgnoreAntiforgeryToken] |
|||
public class ExternalLoginBindModel : AbpPageModel |
|||
{ |
|||
protected AbpSignInManager SignInManager => LazyServiceProvider.LazyGetRequiredService<AbpSignInManager>(); |
|||
|
|||
protected IdentityUserManager UserManager => LazyServiceProvider.LazyGetRequiredService<IdentityUserManager>(); |
|||
|
|||
protected IOptions<IdentityOptions> IdentityOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<IdentityOptions>>(); |
|||
|
|||
public virtual async Task<IActionResult> OnGetAsync() |
|||
{ |
|||
return await Task.FromResult(NotFound()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SPA绑定第三方绑定
|
|||
/// </summary>
|
|||
/// <param name="provider">第三方提供者</param>
|
|||
/// <param name="returnUrl">绑定成功回调地址</param>
|
|||
/// <returns>IActionResult</returns>
|
|||
public virtual async Task<IActionResult> OnPostAsync(string provider, string returnUrl) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(returnUrl)) |
|||
{ |
|||
Logger.LogWarning("The parameter is incorrect"); |
|||
return Redirect(QueryHelpers.AddQueryString(returnUrl, new Dictionary<string, string>() |
|||
{ |
|||
["error"] = "The parameter is incorrect" |
|||
})); |
|||
} |
|||
|
|||
var tenantId = CurrentTenant.Id; |
|||
Logger.LogInformation("CurrentTenant:{TenantId}", tenantId); |
|||
var userId = CurrentUser.GetId(); |
|||
Logger.LogInformation("CurrentUser:{UserId}", userId); |
|||
|
|||
var redirectUrl = Url.Page("ExternalLoginBind", pageHandler: "BindCallback", values: new { returnUrl, userId, tenantId }); |
|||
|
|||
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, userId.ToString()); |
|||
properties.Items["scheme"] = provider; |
|||
|
|||
return await Task.FromResult(Challenge(properties, provider)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SPA绑定第三方登录回调
|
|||
/// </summary>
|
|||
/// <param name="returnUrl">绑定成功回调地址</param>
|
|||
/// <param name="userId">用户Id</param>
|
|||
/// <param name="tenantId">租户Id</param>
|
|||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|||
[UnitOfWork] |
|||
public virtual async Task<IActionResult> OnGetBindCallbackAsync(string returnUrl, string userId, Guid? tenantId) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(returnUrl)) |
|||
{ |
|||
Logger.LogWarning("The returnUrl cannot be empty"); |
|||
return Redirect(QueryHelpers.AddQueryString(returnUrl, new Dictionary<string, string>() |
|||
{ |
|||
["error"] = "The returnUrl cannot be empty" |
|||
})); |
|||
} |
|||
|
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
Logger.LogInformation("CurrentTenant:{TenantId}", tenantId); |
|||
|
|||
await IdentityOptions.SetAsync(); |
|||
|
|||
var loginInfo = await SignInManager.GetExternalLoginInfoAsync(userId); |
|||
if (loginInfo == null) |
|||
{ |
|||
Logger.LogWarning("External login info is not available"); |
|||
return Redirect(QueryHelpers.AddQueryString(returnUrl, new Dictionary<string, string>() |
|||
{ |
|||
["error"] = "External login info is not available." |
|||
})); |
|||
} |
|||
|
|||
await SignInManager.SignOutAsync(); |
|||
|
|||
if (await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey) == null) |
|||
{ |
|||
var externalUser = await UserManager.FindByIdAsync(userId); |
|||
CheckIdentityErrors(await UserManager.AddLoginAsync(externalUser, loginInfo)); |
|||
} |
|||
|
|||
return Redirect(returnUrl); |
|||
} |
|||
} |
|||
|
|||
protected virtual void CheckIdentityErrors(IdentityResult identityResult) |
|||
{ |
|||
if (!identityResult.Succeeded) |
|||
{ |
|||
throw new UserFriendlyException("Operation failed: " + identityResult.Errors.Select(e => $"[{e.Code}] {e.Description}").JoinAsString(", ")); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue