.NET CORE 2.1-如何返回不属于 DefaultChallengeScheme 的挑战

Posted

技术标签:

【中文标题】.NET CORE 2.1-如何返回不属于 DefaultChallengeScheme 的挑战【英文标题】:.NET CORE 2.1- How to return a Challenge which does not belong to the DefaultChallengeScheme 【发布时间】:2020-06-25 10:33:39 【问题描述】:

所以,我有一个场景,我实现了自己的JWT 身份验证方案,并且是我的Startup.cs 中的默认身份验证和质询方案:

services.AddAuthentication(x =>

    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
)
.AddJwtBearer(x =>

    //code ommitted for brevity
)
.AddCookie("cookie")
.AddOpenIdConnect("facbook", async options =>

    options.ResponseType = "code";
    options.SignInScheme = "cookie";
    //code ommitted for brevity
);

正如您在上面看到的,我添加了AddOpenIdConnectAddCookie 用于我的外部身份验证。现在我的问题是,如果我有这样的Redirect ActionMethod,如何返回挑战方案以指向我的外部方案(facebook):

    [HttpGet]
    public async Task<IActionResult> Redirect()
    
        var result = await HttpContext.AuthenticateAsync();

        if (result.Succeeded)
        
            return RedirectToAction("Index");
        

        return Challenge("facebook");
    

这也意味着我的AuthenticateAsync 在这种情况下将不起作用,因为默认身份验证方案指向JWT

如何将 this 添加到我的 Challenge 请求和 AuthenticateAsync 方法中?

谢谢

【问题讨论】:

看来你需要Authorize属性[Authorize(AuthenticationSchemes = "facebook")]:docs.microsoft.com/en-us/aspnet/core/security/authorization/… 【参考方案1】:

要返回自定义身份提供者的登录页面,您需要调用SignInManager.ConfigureExternalAuthenticationProperties() 方法。此方法允许您定义 redirectUrl 和提供者(您将提供者称为“facebook”)。

我是这样写的:

[Controller]
[Route("web/v2/[controller]")]
public class AccountController : Controller

    private IAccountService accountService;
    public AccountController(IAccountService accountService)
    
        this.accountService = accountService;
    

    // GET: web/Account/connect/provider
    [AllowAnonymous]
    [HttpGet("connect/medium/provider", Name = "web-v2-account-external-connect-challenge")]
    public async Task<ActionResult> ExternalLogin([FromRoute]string medium, [FromRoute]string provider)
    
        //var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new  medium, provider );
        var redirectUrl = Url.RouteUrl("web-v2-account-external-connect-callback", new  medium, provider );
        var properties = await accountService.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
        return Challenge(properties, provider);
    

    // GET: web/Account/connect/provider/callback
    [HttpGet("connect/medium/provider/callback", Name = "web-v2-account-external-connect-callback")]
    public async Task<ActionResult> ExternalLoginCallback([FromRoute]string medium, [FromRoute]string provider)
    
        try
        
            var login_result = await accountService.PerfromExternalLogin();
            if (login_result.Status)
            
                var model = new LoginResultVM
                
                    Status = true,
                    Medium = medium,
                    Platform = login_result.Platform
                ;
                return View(model);
            
            else
            
                var model = new LoginResultVM
                
                    Status = false,
                    Medium = medium,
                    Platform = login_result.Platform,

                    Error = login_result.Error,
                    ErrorDescription = login_result.ErrorDescription
                ;
                return View(model);
            
        
        catch (OtherAccountException otherAccountEx)
        
            var model = new LoginResultVM
            
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = otherAccountEx.Message
            ;
            return View(model);
        
        catch (Exception ex)
        
            var model = new LoginResultVM
            
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = "There was an error with your social login"
            ;
            return View(model);
        
    

虽然我的 AccountRepository 看起来像这样:

internal interface IAccountRepository

    ...

internal class AccountRepository : IAccountRepository

    private MintPlayerContext mintplayer_context;
    private UserManager<Entities.User> user_manager;
    private SignInManager<Entities.User> signin_manager;
    private JwtIssuerOptions jwtIssuerOptions;
    public AccountRepository(UserManager<Entities.User> user_manager, SignInManager<Entities.User> signin_manager, MintPlayerContext mintplayer_context, IOptions<JwtIssuerOptions> jwtIssuerOptions)
    
        this.user_manager = user_manager;
        this.signin_manager = signin_manager;
        this.mintplayer_context = mintplayer_context;
        this.jwtIssuerOptions = jwtIssuerOptions.Value;
    

    public async Task<Tuple<User, string>> Register(User user, string password)
    
        ...
    

    public async Task<LoginResult> LocalLogin(string email, string password, bool createCookie)
    
        ...
    

    public async Task<IEnumerable<AuthenticationScheme>> GetExternalLoginProviders()
    
        var providers = await signin_manager.GetExternalAuthenticationSchemesAsync();
        return providers.ToList();
    

    public Task<AuthenticationProperties> ConfigureExternalAuthenticationProperties(string provider, string redirectUrl)
    
        var properties = signin_manager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
        return Task.FromResult(properties);
    

    public async Task<LoginResult> PerfromExternalLogin()
    
        var info = await signin_manager.GetExternalLoginInfoAsync();
        if (info == null)
            throw new UnauthorizedAccessException();

        var user = await user_manager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
        if (user == null)
        
            string username = info.Principal.FindFirstValue(ClaimTypes.Name);
            string email = info.Principal.FindFirstValue(ClaimTypes.Email);

            var new_user = new Entities.User
            
                UserName = username,
                Email = email,
                PictureUrl = null
            ;
            var id_result = await user_manager.CreateAsync(new_user);
            if (id_result.Succeeded)
            
                user = new_user;
            
            else
            
                // User creation failed, probably because the email address is already present in the database
                if (id_result.Errors.Any(e => e.Code == "DuplicateEmail"))
                
                    var existing = await user_manager.FindByEmailAsync(email);
                    var existing_logins = await user_manager.GetLoginsAsync(existing);

                    if (existing_logins.Any())
                    
                        throw new OtherAccountException(existing_logins);
                    
                    else
                    
                        throw new Exception("Could not create account from social profile");
                    
                
                else
                
                    throw new Exception("Could not create account from social profile");
                
            

            await user_manager.AddLoginAsync(user, new UserLoginInfo(info.LoginProvider, info.ProviderKey, info.ProviderDisplayName));
        

        await signin_manager.SignInAsync(user, true);
        return new LoginResult
        
            Status = true,
            Platform = info.LoginProvider,
            User = ToDto(user)
        ;
    

    public async Task<IEnumerable<UserLoginInfo>> GetExternalLogins(ClaimsPrincipal userProperty)
    
        ...
    

    public async Task AddExternalLogin(ClaimsPrincipal userProperty)
    
        ...
    

    public async Task RemoveExternalLogin(ClaimsPrincipal userProperty, string provider)
    
        ...
    

    public async Task<User> GetCurrentUser(ClaimsPrincipal userProperty)
    
        var user = await user_manager.GetUserAsync(userProperty);
        return ToDto(user);
    

    public async Task Logout()
    
        await signin_manager.SignOutAsync();
    

    #region Helper methods
    private string CreateToken(Entities.User user)
    
        ...
    
    #endregion
    #region Conversion methods
    internal static Entities.User ToEntity(User user)
    
        ...
    
    internal static User ToDto(Entities.User user)
    
        ...
    
    #endregion

【讨论】:

以上是关于.NET CORE 2.1-如何返回不属于 DefaultChallengeScheme 的挑战的主要内容,如果未能解决你的问题,请参考以下文章

.Net Core 2.1 过期的 JWT 令牌响应 [发布与获取]

IFormFile 在 asp.net core 2.1 中总是返回 null

ASP.NET Core 2.1 Razor 页面返回带有模型的页面

JQuery 在 ASP.NET Core 2.1 中返回“未定义”值

.NET Core 2.1 中的 HttpContext.GetTokenAsync 无法检索 JWT

如何使用 Moq 在 .NET Core 2.1 中模拟新的 HttpClientFactory