关于认证
参考ASP.NET Core 中的那些认证中间件及一些重要知识点
在 ASP.NET Core 中,身份验证由 IAuthenticationService 负责,而它供身份验证中间件使用。
身份验证中间件
已注册的身份验证处理程序及其配置选项被称为“方案(schema)”。
Authentication schemes are specified by registering authentication services in Startup.ConfigureServices:
在startup.cs的ConfigureServices中通过注册身份认证,指定认证方案
2
3
4
5
services.AddAuthentication("YourSchemaName")
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => Configuration.Bind("JwtSettings", options))
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => Configuration.Bind("CookieSettings", options));
}
AddAuthentication的参数是方案名称,默认值为JwtBearerDefaults.AuthenticationScheme(即”Bearer”)。
可使用多种身份验证方案1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public void ConfigureServices(IServiceCollection services)
{
    // 认证方案
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://localhost:5000/identity/";
        })
        .AddJwtBearer("AzureAD", options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://login.microsoftonline.com/eb971100-6f99-4bdc-8611-1bc8edd7f436/";
        });
    // 授权访问
    services.AddAuthorization(options =>
    {
        var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
            JwtBearerDefaults.AuthenticationScheme,
            "AzureAD");
        defaultAuthorizationPolicyBuilder = 
            defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
        options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
    });
}
再说方案名称(AuthenticationScheme),可使用方案名称来指定应使用哪种(或哪些)身份验证方案来对用户进行身份验证。 当配置身份验证时,通常是指定默认身份验证方案。除非资源请求了特定方案,否则使用默认方案。
授权策略(authorization policy)
下文中有使用特性注解为资源(如api)指定授权方案的栗子
自定义策略提供程序-IAuthorizationPolicyProvider
文章Asp.Net Basic Authentication自定义了使用Basic Auth进行认证的方案,配置方案如1
2services.AddAuthentication("BasicAuthentication")
    .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
BasicAuthenticationHandler是自定义的身份验证处理类,派生自AuthenticationHandler\1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly IUserService _userService;
    public BasicAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        IUserService userService)
        : base(options, logger, encoder, clock)
    {
        _userService = userService;
    }
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.ContainsKey("Authorization"))
            return AuthenticateResult.Fail("Missing Authorization Header");
        User user = null;
        try
        {
            var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
            var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
            var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
            var username = credentials[0];
            var password = credentials[1];
            user = await _userService.Authenticate(username, password);
        }
        catch
        {
            return AuthenticateResult.Fail("Invalid Authorization Header");
        }
        if (user == null)
            return AuthenticateResult.Fail("Invalid Username or Password");
        var claims = new[] {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.Username),
        };
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);
        return AuthenticateResult.Success(ticket);
    }
}
类型定义身份验证操作,负责根据请求上下文构造用户的身份。 返回一个 AuthenticateResult指示身份验证是否成功
选择具有策略的方案
| 1 | .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => Configuration.Bind("JwtSettings", options)) | 
ASP.NET Core 中的授权通过 AuthorizeAttribute 和其各种参数来控制。 在最简单的形式中,将 [Authorize] 属性应用于控制器、操作或 Razor 页面,将对该组件的访问限制为任何经过身份验证的用户。
2
3
4
5
6
7
8
9
10
{
[Authorize]
[Route("api/v1/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
......
}
}
允许未通过验证的访问—AllowAnonymous
2
3
4
5
6
7
8
9
10
11
12
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
}
Authorize从controller到action向下继承,而AllowAnonymous覆盖Authorize(AllowAnonymous优先级高于Authorize)JwtBearer
持有者身份验证
Basic Auth
Cors
基于策略的授权
startup.cs1
2
3
4
5
6
7
8
9
10public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("PolicyBased01", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });
}
指定特定授权方案
参考 Authorize with a specific scheme in ASP.NET Core
An authentication scheme is named when the authentication service is configured during authentication. Startup的服务配置中, AddAuthentication后添加方案
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
// Code omitted for brevity
services.AddAuthentication()
.AddCookie(options => {
options.LoginPath = "/Account/Unauthorized/";
options.AccessDeniedPath = "/Account/Forbidden/";
})
.AddJwtBearer(options => {
options.Audience = "http://localhost:5001/";
options.Authority = "http://localhost:5000/";
});
....
}
使用授权属性选择方案
2
3
public class MixedController : Controller
.....
使用策略并指定授权方案
If you prefer to specify the desired schemes in policy, you can set the AuthenticationSchemes collection when adding your policy. 添加授权策略时设置AuthenticationSchemes列表,将对应的scheme添加进去
2
3
4
5
6
7
8
9
{
options.AddPolicy("Over18", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
policy.Requirements.Add(new MinimumAgeRequirement());
});
});
在属性中使用指定策略
2
public class RegistrationController : Controller
使用多种方案
the following code in Startup.ConfigureServices adds two JWT bearer authentication schemes with different issuers:颁发者不同的两种JWT Bearer认证方案
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
// Code omitted for brevity
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://localhost:5000/identity/";
})
.AddJwtBearer("AzureAD", options =>
{
options.Audience = "https://localhost:5000/";
options.Authority = "https://login.microsoftonline.com/eb971100-6f99-4bdc-8611-1bc8edd7f436/";
});
...
}
更新授权策略, 上面两种JWTBearer认证方案,将一个要注册到默认认证方案“JwtBearerDefaults.AuthenticationScheme”,另外的认证方案需要以唯一的方案注册(Only one JWT bearer authentication is registered with the default authentication scheme JwtBearerDefaults.AuthenticationScheme. Additional authentication has to be registered with a unique authentication scheme.)
2
3
4
5
6
7
8
9
10
11
12
13
14
{
// Code omitted for brevity
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
"AzureAD");
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
}
参考在 ASP.NET Core 中使用 IAuthorizationPolicyProvider 的自定义授权策略提供程序