【问题标题】:How do I set up authentication & authorisation for ASP .NET Core 3.0 with PageModels?如何使用 PageModels 为 ASP .NET Core 3.0 设置身份验证和授权?
【发布时间】:2020-01-01 07:42:14
【问题描述】:

我正在尝试为我拥有的前端 Web 应用程序设置身份验证和授权部分。该应用程序设置为 ASP.NET Core Razor 页面应用程序。 (.NET 核心 3.0);我还使用以下命令预先安装了身份验证:dotnet new razor -au Individual

有了这个,我正在尝试从我拥有的外部服务(身份服务器)设置 OpenID 身份验证外部登录。问题在于在前端显示最终用户(或者如果我这样做完全错误...... - 我在这个主题上花费了有问题的时间,除了旧问题我找不到任何东西,或有关您以前如何使用 MVC 应用程序执行此操作的内容)- 我还能够找到存储在本地数据库中的外部用户,并且可以通过从外部身份服务器成功登录的代码来判断。 (稍后解释)

这是我的第一个具有主要身份验证/授权部分的项目 - 如果出现任何愚蠢的问题,我提前道歉。我很想知道出了什么问题。来了!

Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseMySql(Configuration.GetConnectionString("DefaultConnection")
                ));

            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();


            services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                //options.DefaultAuthenticateScheme = IdentityConstants.ExternalScheme;
                //options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
                options.DefaultChallengeScheme = "oidc"; 

            })
                    .AddCookie(options =>
                    {
                        options.LoginPath = "/Account/Login/";
                    })
                    .AddOpenIdConnect(ChallengeScheme, o =>
                    {
                        o.ClientId = "client_id";
                        o.ClientSecret = "*******************";
                        o.Authority = "http://endpointhere.com";
                        o.ResponseType = "code" ;
                        o.SaveTokens = true;
                        o.Scope.Add("IdentityServerApi");
                    });


            services.AddAuthorization();
            services.AddRazorPages();
        }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }


            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
            });
        }

ExternalLogin.cshtml.cs; OnGetCallbackAsync

public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            if (remoteError != null)
            {
                ErrorMessage = $"Error from external provider: {remoteError}";
                return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
            }
            var info = await _signInManager.GetExternalLoginInfoAsync();

            if (info == null)
            {
                ErrorMessage = "Error loading external login information.";
                return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
            }

            // Sign in the user with this external login provider if the user already has a login.
            var result = await _signInManager.ExternalLoginSignInAsync("oidc", info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
            if (result.Succeeded)
            {
                // Store access token, token so it is included in cookie
                var user = await _userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
                var props = new AuthenticationProperties();
                props.StoreTokens(info.AuthenticationTokens);
                //await _signInManager.SignInAsync(user, props, info.LoginProvider);
                // Update external authentication tokens with signInManager
                await _signInManager.UpdateExternalAuthenticationTokensAsync(info);


                _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
                return LocalRedirect(returnUrl);
            }
            if (result.IsLockedOut)
            {
                return RedirectToPage("./Lockout");
            }
            else
            {
                // If the user does not have an account, then ask the user to create an account.
                ReturnUrl = returnUrl;
                LoginProvider = info.LoginProvider;
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
                {
                    Input = new InputModel
                    {
                        Email = info.Principal.FindFirstValue(ClaimTypes.Email)
                    };
                }
                return Page();
            }
        }

_LoginPartial.cshtml

@using Microsoft.AspNetCore.Identity

@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (User.Identity.IsAuthenticated)
{
    <li class="nav-item">
        <a  class="nav-link text-light" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/", new { area = "" })" method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-light" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-light" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

从上面的源代码:成功登录后,我能够检索从外部身份服务器检索到的访问令牌 - 但我无法在 UI 中加载任何用户信息。

例如,在_LoginPartial 中,User.Identity.IsAuthenticatedSignInManager.IsSignedIn(User) 中的默认值都不是真的。它们都总是假的。

我看了下面的代码,然后……:

// Code from ExternalLogin.cshtml.cs
var info = await _signInManager.GetExternalLoginInfoAsync();

info.Principal.Identity.IsAuthenticated 总是返回 true;我还阅读了这些资源,试图弄清楚发生了什么——我尝试了很多东西,但无济于事。 PS:我也去尝试了旧的 MVC 方式——但是我在自己的 mac 上无法做任何脚手架,所以我在 GitHub 上提出了一个问题:https://github.com/aspnet/Identity/issues/1452

现在,我还了解到,从this issue 我知道 User.Identity.IsAuthenticated 用于其他任何外部,而 SignInManager 仅用于 asp.net 的身份框架。我只是想知道这是否与我的问题有关?

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-core asp.net-identity


    【解决方案1】:

    但我无法在 UI 中加载任何用户信息。

    注意当某些用户通过身份验证后被重定向到的URL

    /Identity/Account/ExternalLogin?returnUrl=%2F&handler=Callback
    

    Identity/Account/ExternalLogin 页面将使用Identity.Application 的方案通过以下方式发送cookie 来登录用户:

    set-cookie: .AspNetCore.Identity.Application={the-cookie-here}
    

    换句话说,这里的登录方案是IdentityConstants.ApplicationScheme(即Identity.Application

    但是,您已将默认身份验证方案设置为 Cookies

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        ...
    })
    

    因此,默认情况下,RazorPage 中的 User 属性将始终使用 CookieAuthenticationDefaults.AuthenticationScheme 的方案进行身份验证,这与 Identity.Application 方案不同。这就是为什么你的User.Identity.IsAuthenticated 总是假的。

    要解决这个问题,您可以:

    1. 方法 1:为 Cookie 配置转发方案: .AddCookie(选项 =>{ options.LoginPath = "/帐户/登录/"; options.ForwardDefault = IdentityConstants.ApplicationScheme; })
    2. 方法二:使用IdentityConstants.ApplicationScheme作为默认方案
    3. 方法3:为页面处理程序/页面模型手动添加[Authorize(AuthenticationSchemes="Identity.Application")]: [授权(AuthenticationSchemes="Identity.Application")]

    【讨论】:

    • 非常感谢。我想我遇到的问题是使用了错误的默认方案,我应该使用 IdentityConstants.ApplicationScheme - 这将有助于填充 User.identity 对象?再次感谢您!
    • @BrianChew 是的。这就是为什么我建议使用方法 2。使用正确的默认方案,User.Identity 将被正确填充。
    猜你喜欢
    • 2018-12-15
    • 1970-01-01
    • 2021-06-15
    • 1970-01-01
    • 2018-12-20
    • 2020-03-09
    • 1970-01-01
    • 2021-04-16
    • 2023-04-01
    相关资源
    最近更新 更多