Conditional AccessDeniedPath in ASP.NET Core 3.1



我的项目有两个控制器来支持不同角色的用户-成员顾问登录时,我设置";角色">ClaimType

会员和顾问有一个不同的登录页面,在登录后,MemberController

ConsultantController顾问控制器.CS

[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 });
}

启动.CS

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);
}
}

问题

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

我尝试了King King的方法,通过自定义CookieAuthenticationHandler来覆盖HandleForbiddenAnc,但代码从未执行。

这是因为还没有登录的用户是"strong";未经授权的"。如果他们试图访问[授权]资源,则会将用户定向到登录路径,而不是AccessDeniedPath。这对应于HTTP请求方面的401。

用户是"strong";被禁止的"如果他们已经登录,但他们使用的标识没有查看请求资源的权限,这对应于HTTP中的403。

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

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

解决方案

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

我已经更新了以下代码,以纳入KingKing的反馈/评论。这包括使用StartsWithSegments而不仅仅是Path.ToString((。包含.

同样根据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();
});

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

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

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

相关内容

  • 没有找到相关文章

最新更新