NET6完整项目实战系列第6篇:用户登录番外篇--app.UseAuthentication()和app.UseAuthorization()的使用(下)
要达到最初的设想,需在 Program.cs中启用认证中间件服务 : app.UseAuthentication();
同时给 builder.Services.AddAuthentication( ) 处理程序设置对应的方案,如下(红色部分代码):
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days.
// You may want to change this for production scenarios,
// see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
//app.UseAuthorization();
app.MapRazorPages();
app.Run();
编译后重新运行,在登录页面输入 admin/123 后运行结果如下:
可以看到用户已通过了认证,声明中的值也都打印出来了,至此,整个登录认证过程就完成了。
补充:前面提到“方案”这个词,对应的英文是 Scheme , 那么什么是方案呢?看微软官方文档是如何描述的:
身份验证方案及处理程序的功能在官方文档描述如下:
至于如何去自定义,后续再展开。
接下来,我们来看如何实现不同的角色访问不同的页面。
虽然前面实现了认证,能读取声明中的信息,但以admin的身份登录后是可以访问 UserIndex 页面的,反之亦然,要实现admin账号
只能访问adminmng目录下的页面,步骤如下:
第1步:在Program.cs中启用授权中间件并设置无权限访问时要跳转的页面,本例中跳转到 Forbidden.cshtml ,代码件红色字体部分:
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => {
//滑动过期
options.SlidingExpiration = true;
//滑动过期时间30分钟,即30分钟内没有访问页面就过期
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
//无权限访问时跳转到的页面,Pages目录下要有Forbidden.cshtml页面
options.AccessDeniedPath = "/forbidden";
});
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days.
// You may want to change this for production scenarios,
// see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
//启用授权中间件
app.UseAuthorization();
app.MapRazorPages();
app.Run();
第2步:在 Pages 目录下新增 Forbidden.cshtml ,页面代码如下:
@page
@model WebApplication1.Pages.ForbiddenModel
@{
}
<div>没有权限访问此页面!</div>
第3步:给要设置权限的页面 Model 增加Authorize特性,如下(见红色字体):
AdminIndex.cshtml.cs 页面如下:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication1.Pages.AdminMng
{
[Authorize(Roles ="AdminRole")]
public class AdminIndexModel : PageModel
{
public void OnGet()
{
}
}
}
UserIndex.cshtml.cs 页面如下:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication1.Pages.UserMng
{
[Authorize(Roles = "UserRole")]
public class UserIndexModel : PageModel
{
public void OnGet()
{
}
}
}
至此配置完毕, 最重要的是在登录声明中一定要包含 new Claim(ClaimTypes.Role, roleName) 语句 !
注意:和 WEBAPI 不同,RazorPages 的角色授权只能是整个页面(这里是UserIndexModel),不可以具体到页面中的某个方法,
而WebAPI既可以是Controller,也可以是Controller中的某个方法(Action)。
解决方案目录如下:
编译后运行,访问以admin的账号登录,页面如下:
在浏览器的地址栏把网址改成 usermng/userindex 后按 enter 键 ,页面如下:
可以看到:
1. 网址变成了 forbidden ,并且我们欲访问的页面 usermng/userindex 变成了查询参数 ReturnUrl 的值;
2. 显示了 Forbidden.cshtml 页面的值,这是我们在 Program.cs 的 AddCookie( ) 方法中设置的;
3. 按下 enter 键后访问的页面返回了 302 的状态码, 然后页面发生了跳转;
4. cookie 信息没有改变;
=================结束分割线==============
问题: 如果有一个页面 ,比如 Pages/Common/ModifyPassword.cshtml 只要登录用户都可以访问,不需要区分角色,该如何设置呢?
有2种方法:
其一:在 ModifyPassword.cshtml.cs 中增加 Authorize 特性且不要传递 Roles 属性值,如下(红色字体部分):
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication1.Pages.Common
{
[Authorize]
public class ModifyPasswordModel : PageModel
{
public void OnGet()
{
}
}
}
重新编译后清除cookie访问如下:
其二:在 program.cs 中增加 授权约定(authorization conventions) ,如下(红色字体部分):
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => {
//滑动过期
options.SlidingExpiration = true;
//滑动过期时间30分钟,即30分钟内没有访问页面就过期
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
//无权限访问时跳转到的页面,Pages目录下要有Forbidden.cshtml页面
options.AccessDeniedPath = "/forbidden";
//无认证时跳转的页面
options.LoginPath = "/login";
});
// Add services to the container.
builder.Services.AddRazorPages(options => {
//针对文件设置访问权限
options.Conventions.AuthorizePage("/Common/ModifyPasswrod");
//针对文件夹设置访问权限
//options.Conventions.AuthorizeFolder("/Common");
//以下设置可以针对文件或文件夹取消访问权限验证
//options.Conventions.AllowAnonymousToPage("/Common/ModifyPasswrod");
//options.Conventions.AllowAnonymousToFolder("/Common");
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days.
// You may want to change this for production scenarios,
// see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
编译后访问,和第一种方式的效果相同。。。
注意:文件夹和页面授权的包含关系如下:
* 先指定页面文件夹需要授权,然后指定该文件夹中的页面允许匿名访问,这样操作是有效的:
.AuthorizeFolder("/Common").AllowAnonymousToPage("/Common/XxxPage") // 正确 !
* 不可以先为匿名访问声明页面文件夹,然后在该文件夹中指定需要授权的页面
.AllowAnonymousToFolder("/Common").AuthorizePage("/Common/XxxPage") // 错误!
【完】
【 源码下载】