0

我的项目有两个控制器来支持来自不同角色的用户 -成员顾问。在登录时,我为每个设置了“角色” ClaimType

会员和顾问有一个不同的登录页面,登录后MemberControllerConsultantController都重定向到“桌面”操作。

顾问控制器

    [HttpPost()]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> SignIn(SignIn sin)
    {
        try
        {
            // check authorisation
            if (ModelState.IsValid)
            {
                sin = await RepoSamadhi.ShopSignIn(sin);
                if (sin.ShopID == 0 || sin.IsValidationFail || string.IsNullOrEmpty(sin.ShopToken))
                {
                    is_err = true;
                    _logger.LogInformation("Consultant SignIn Invalid Credentials", sin.EmailAddress);                        
                    ModelState.AddModelError("Consultant", "Account not found. Check your credentials.");
                }
            }                
            else
            {
                sin.IsSignInFailed = true;
                return View("SignIn", sin);
            }

            // create claims
            var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Sid, sin.ShopToken),
            new Claim(ClaimTypes.NameIdentifier, sin.ShopID.ToString()),
            new Claim(ClaimTypes.Email, sin.EmailAddress.ToLower()),
            new Claim(ClaimTypes.Role, "Consultant")
        };

            // create identity
            var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); // cookie or local            

            // create principal
            ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme));

            // create auth properties
            var authProperties = new AuthenticationProperties
            {
                IsPersistent = sin.RememberMe;
            };

            // sign-in
            await HttpContext.SignInAsync(scheme: CookieAuthenticationDefaults.AuthenticationScheme, principal: principal, properties: authProperties);
        }
        catch (Exception ex)
        {
            gFunc.ProcessError(ex);
        }
        return RedirectToAction("Desktop", new { date = DateTime.Today.ToString("d MMM yyyy"), timer = false });
    }

启动程序

    public void ConfigureServices(IServiceCollection services)
    {
        try
        {
            services.AddRazorPages()
                .AddRazorRuntimeCompilation();

            services.AddControllersWithViews();

            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
                {
                    options.ExpireTimeSpan = new TimeSpan(30, 0, 0, 0);
                    options.LoginPath = new PathString("/Home/Index/");
                    options.AccessDeniedPath = new PathString("/Home/Index/");
                    options.LogoutPath = new PathString("/Home/Index/");
                    options.Validate();
                });

            services.Configure<Microsoft.AspNetCore.Identity.IdentityOptions>(options =>
            {
                options.Password.RequireDigit = true;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = true;
                options.Password.RequireUppercase = true;
                options.Password.RequiredLength = 8;
                options.Password.RequiredUniqueChars = 1;
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                options.Lockout.MaxFailedAccessAttempts = 5;
                options.Lockout.AllowedForNewUsers = true;
                options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
                options.User.RequireUniqueEmail = false;
            });

            // add detection services container and device resolver service
            services.AddDetectionCore()
                .AddDevice();

            services.AddMvc();
            services.AddAntiforgery();
            services.Configure<MvcOptions>(options =>
            {
                options.Filters.Add(new RequireHttpsAttribute());
            });
        }
        catch (Exception ex)
        {
            gFunc.ProcessError(ex);
        }
    }

问题

如何配置身份验证服务以在用户尝试访问授权资源时将用户重定向到正确的登录页面)但未登录(即没有有效的身份验证 cookie)?目前我只有一个“AccessDeniedPath”,它将用户带到主页。

4

2 回答 2

1

我通过自定义CookieAuthenticationHandler来覆盖HandleForbiddenAsync尝试了 King King 的方法,但代码永远不会执行。

这是因为尚未登录的用户是“未经授权的”。如果他们尝试访问 [Authorize] 资源,用户将被定向到LoginPath,而不是 AccessDeniedPath。这对应于 HTTP 请求的 401。

如果用户已经登录,但他们使用的身份没有查看请求资源的权限,则用户被“禁止” ,这对应于 HTTP 中的 403。

在 MS 文档中:“ AccessDeniedPath获取或设置如果用户不批准远程服务器请求的授权请求,则用户代理重定向到的可选路径。默认情况下未设置此属性。在这种情况下,抛出异常如果远程授权服务器返回 access_denied 响应。”

因此,在登录并随后请求没有所需角色的受保护资源(即用 [Authorize(Roles = "MyRole")] 装饰的操作)后,应重定向到配置的 AccessDeniedPath。在这种情况下,我应该能够使用 King King 的方法.

解决方案

最后,我只是向CookieAuthenticationOptions事件 ( OnRedirectToLogin ) 添加了一个委托。

我已经更新了以下代码以合并来自 KingKing 的反馈/评论。这包括使用StartsWithSegments而不仅仅是 Path.ToString().Contains。

同样根据 KK 的建议,我捕获了默认回调,然后在返回中使用它。

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
{
     o.ExpireTimeSpan = new TimeSpan(90, 0, 0, 0);
     o.AccessDeniedPath = new PathString("/Samadhi/SignIn/");
     o.LoginPath = new PathString("/Samadhi/SignIn/");
     o.LogoutPath = new PathString("/Samadhi/SignOut/");
     var defaultCallback = o.Events.OnRedirectToLogin;
     o.Events.OnRedirectToLogin = context =>
     {
          if (context.Request.Path.StartsWithSegments(new PathString("/member"), StringComparison.OrdinalIgnoreCase))
          {
               context.RedirectUri = "/Member/SignIn/";
               context.Response.Redirect(context.RedirectUri);
          }
          else if (context.Request.Path.StartsWithSegments(new PathString("/consultant"), StringComparison.OrdinalIgnoreCase))
          {
               context.RedirectUri = "/Consultant/SignIn/";
               context.Response.Redirect(context.RedirectUri);
          }
          return defaultCallback(context);
    };
    o.Validate();
});
于 2021-03-24T15:28:36.793 回答
0

在我看来,主要问题是如果用户不包含令牌,您如何知道当前登录用户?

在我看来,我建议您首先可以使用主登录页面。然后,如果用户输入了其用户名,您可以使用 ajax 之类的 js 来检查服务器中的用户名或电子邮件。

如果用户是 Members ,那么您可以在 ajax 成功方法中编写逻辑以将用户重定向到 Members 登录页面。

于 2021-03-24T06:07:33.653 回答