前言

Cookie/Session模式下的登录验证

Action/控制器上加上特性

1
2
[Authorize]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]//指定渠道
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//使用Session
builder.Services.AddSession();

//选择使用哪种方式来鉴权
builder.Services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultChallengeScheme= CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultForbidScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option =>
{
//如果没有找到用户信息,鉴权失败/授权失败,则跳转到指定的Action
//option.LoginPath = "/Account/Login";

//能找到用户信息,但是Roles不匹配,则跳转到指定的Action
//option.AccessDeniedPath = "/Account/AccessDenied";
});
...

app.UseAuthentication();//鉴权
app.UseAuthorization();//授权

登录页面

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
<h1>Login</h1>

<div class="col-md-8">
<section id="loginForm">
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = Context.Request.Query["ReturnUrl"] }, FormMethod.Post, true, new { @class = "form-horizeontal", role = "form" }))
{
@Html.AntiForgeryToken()
<hr />
@Html.ValidationSummary(true)


<div class="mb-3 row">
@Html.Label("Name","用户名:",new {@class="col-sm-3 col-form-label"})
<div class="col-md-6">
@Html.TextBox("Name",null,new {@class="form-control", @placeholder="请输入用户名..."})
</div>
</div>

<div class="mb-3 row">
@Html.Label("Password","密码:",new {@class="col-sm-3 col-form-label"})
<div class="col-md-6">
@Html.Password("Password",null,new {@class="form-control", @placeholder="请输入密码..."})
</div>
</div>

<div class="mb-3 row">
<div class="col-md-offset-2 col-md-6">
<button type="submit" class="btn btn-primary mb-3">登录</button>
@base.ViewBag.Msg
</div>
</div>
}
</section>
</div>

登录逻辑

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
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(string ReturnUrl, string name, string password)
{
if (name == "admin" && password == "123")
{
var claims = new List<Claim>() {
new Claim("UserId","1"),
new Claim(ClaimTypes.Role,"Admin"),
new Claim(ClaimTypes.Role,"User"),
new Claim(ClaimTypes.Name,$"{name}---来自Cookie")
};

ClaimsPrincipal userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Customer"));
//过期时间:30分钟
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, userPrincipal, new AuthenticationProperties { ExpiresUtc = DateTime.UtcNow.AddMinutes(30) }).Wait();

var user = HttpContext.User;

return string.IsNullOrEmpty(ReturnUrl) ? base.RedirectToAction("Index") : base.Redirect(ReturnUrl);

}
else
{
base.ViewBag.Msg = "用户名或密码错误";
}

return View();
}

角色验证

Roles 严格区分大小写

当前角色必须拥有Admin 并且 User,才允许访问

1
2
3
4
5
6
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Roles = "Admin")]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Roles = "User")]
public IActionResult Index()
{
return View();
}

当前角色拥有 Admin 或者 User 则允许访问

1
2
3
4
5
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Roles = "Admin,User")]
public IActionResult Index()
{
return View();
}

策略授权

1
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme, Policy = "rolePolcy")]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//策略授权
builder.Services.AddAuthorization(options => {
options.AddPolicy("rolePolcy", policyBuilder => {

// 1.Role
//policyBuilder.RequireRole("Admin");

//2.必须包含某个Claim
//policyBuilder.RequireClaim("UserId");

//3. 自定义
policyBuilder.RequireAssertion(handler => {
return
handler.User.HasClaim(x => x.Type == ClaimTypes.Role)
&& handler.User.Claims.Any(x => ClaimTypes.Role.Equals(x.Type) && "Admin".Equals(x.Value));
});
});
});

自定义授权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//3. 自定义
policyBuilder.RequireAssertion(handler => {
return
handler.User.HasClaim(x => x.Type == ClaimTypes.Role);
//&& handler.User.Claims.Any(x => ClaimTypes.Role.Equals(x.Type) && "Vip".Equals(x.Value));
});
//先验证自定义策略,再执行当前自定义的
policyBuilder.AddRequirements(new MyUserAuthorizationRequirement());

...

//注入
builder.Services.AddTransient<IUserService, UserService>();
builder.Services.AddTransient<IAuthorizationHandler, UserHander>();

MyUserAuthorizationRequirement.cs

1
2
3
public class MyUserAuthorizationRequirement: IAuthorizationRequirement
{
}

UserHander.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserHander : AuthorizationHandler<MyUserAuthorizationRequirement>
{
private readonly IUserService _userService;
public UserHander(IUserService userService) {
_userService = userService;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyUserAuthorizationRequirement requirement)
{
var user = context.User;
if (_userService.ValidateUser())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}

IUserService.cs

1
2
3
4
public interface IUserService
{
public bool ValidateUser();
}

UserService.cs

1
2
3
4
5
6
7
8
public class UserService : IUserService
{
public bool ValidateUser()
{
//验证逻辑
return true;
}
}