如何解决ASP.NET Core 3.1 中的条件访问拒绝路径
我的项目有两个控制器来支持来自不同角色的用户 - 成员和顾问。登录时我设置了“角色”ClaimType 每个。
会员和顾问有不同的登录页面,登录后 MemberController 和 ConsultantController 都重定向到“桌面”操作。
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 });
}
STARTUP.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);
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”,它会将用户带到主页。
解决方法
我尝试了 King King 的方法,通过自定义 CookieAuthenticationHandler 来覆盖 HandleForbiddenAsync,但代码永远不会执行。
这是因为尚未登录的用户“未经授权”。如果他们尝试访问 [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);
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 success 方法中编写逻辑,将用户重定向到 Member 登录页面。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。