ASP.NET Core 鉴权授权
大约 6 分钟约 1894 字
ASP.NET Core 鉴权授权
演示源码链接:https://pan.baidu.com/s/1Qimlu7p0l2HFMSyfLsN-HQ?pwd=iccf
鉴权
Cookies
- 参照SunnyFan.Author.Cookies
- Program.cs 注册服务处理
builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) //凭证在哪里
.AddCookie(options =>
{
options.Events.OnRedirectToLogin = async context =>
{
var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json; charset=utf-8";
await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
};
});
app.UseRouting();
app.UseAuthentication();//鉴权中间件---表明请求过程中,需要鉴权
app.UseAuthorization();//授权中间件
- AuthorController.cs 登录模拟
{
/// <summary>
/// 授权
/// </summary>
[ApiController]
[Route("[controller]/[action]")]
public class AuthorController : ControllerBase
{
/// <summary>
///
/// </summary>
public AuthorController()
{
}
/// <summary>
/// 登录
/// </summary>
/// <param name="loginDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> LoginAsync([FromBody] LoginDto loginDto)
{
//登录逻辑
var claimIdentity = new ClaimsIdentity("Custom");
claimIdentity.AddClaim(new Claim(ClaimTypes.Name, loginDto.Account!));
claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "Chinese"));
await base.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimIdentity), new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(30),
});//登陆默认Scheme,写入Cookie
return new JsonResult(new { code = 200, msg = "success" });
}
}
/// <summary>
/// 登录对象
/// </summary>
public class LoginDto
{
/// <summary>
/// 账户
/// </summary>
public string? Account { get; set; }
/// <summary>
/// 密码
/// </summary>
public string? Pwd { get; set; }
}
}
- UserController.cs 授权测试 Authorize
{
/// <summary>
/// 用户
/// </summary>
[ApiController]
[Route("[controller]/[action]")]
public class UserController : ControllerBase
{
/// <summary>
///
/// </summary>
public UserController()
{
}
/// <summary>
/// 获取用户详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}")]
[Authorize]
public async Task<IActionResult> GetAsync(int id)
{
await Task.CompletedTask;
return new JsonResult(new
{
code = 200,
msg = "success",
data = new { id = 1, account = "zhangsan", name = "张三" }
});
}
}
}
- 测试效果
JWT
- 参照SunnyFan.Author.JWT
- Program.cs 注册服务处理
//注册服务
ConfigurationManager configuration = builder.Configuration;
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true, //是否验证Issuer
ValidIssuer = configuration["Jwt:Issuer"], //发行人Issuer
ValidateAudience = true, //是否验证Audience
ValidAudience = configuration["Jwt:Audience"], //订阅人Audience
ValidateIssuerSigningKey = true, //是否验证SecurityKey
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"])), //SecurityKey
ValidateLifetime = true, //是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
RequireExpirationTime = true,
AudienceValidator = (m, n, z) =>
{
//等同于去扩展了下 Audience的校验规则---鉴权--可以让我们在这里写判断逻辑
return true;
},
LifetimeValidator = (notBefore, expires, securityToken, validationParameters) =>
{
return true;
}
};
//在校验token的过程中,会有很多环节---可以通过注册时间来监听每个环节
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = async context =>
{
//身份验证失败时触发
Console.WriteLine("this is OnAuthenticationFailed");
await context.HttpContext.Response.WriteAsync("{ Success=false,Message='授权失败了' } ");
await Task.CompletedTask;
},
OnForbidden = async context =>
{
//OnForbidden 时 触发 //授权失败后触发
Console.WriteLine("this is OnForbidden");
await Task.CompletedTask;
},
OnMessageReceived = async context =>
{
string authHeader = context.HttpContext.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader) && authHeader.Length > 7)
{
authHeader = authHeader.Substring(7);
context.Token = authHeader;
}
//收到信息时 触发
Console.WriteLine("this is OnMessageReceived");
await Task.CompletedTask;
},
OnTokenValidated = async context =>
{
//验证之后触发
Console.WriteLine("this is OnTokenValidated");
await Task.CompletedTask;
},
OnChallenge = async context => //最终输出的时候,都会触发
{
Console.WriteLine("this is OnChallenge");
await Task.CompletedTask;
}
};
});
app.UseRouting();
app.UseAuthentication();//鉴权中间件---表明请求过程中,需要鉴权
app.UseAuthorization();//授权中间件
- AuthorController.cs 登录模拟
{
/// <summary>
/// 授权
/// </summary>
[ApiController]
[Route("[controller]")]
public class AuthorController : ControllerBase
{
/// <summary>
///
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
///
/// </summary>
public AuthorController(IConfiguration configuration)
{
_configuration = configuration;
}
/// <summary>
/// 登录
/// </summary>
/// <param name="loginDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> LoginAsync([FromBody] LoginDto loginDto)
{
ClaimsIdentity claimIdentity = new ClaimsIdentity("Custom");
claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "SunnyFan"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "Chinese"));
SymmetricSecurityKey secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
SigningCredentials creds = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
//Nuget引入:System.IdentityModel.Tokens.Jwt
JwtSecurityToken token = new JwtSecurityToken(
issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Audience"],
claims: claimIdentity.Claims,
expires: DateTime.Now.AddMinutes(5),//5分钟有效期
signingCredentials: creds);
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
await Task.CompletedTask;
return new JsonResult(new { code = 200, msg = "success", data = new { token = returnToken } });
}
}
/// <summary>
/// 登录对象
/// </summary>
public class LoginDto
{
/// <summary>
/// 账户
/// </summary>
public string? Account { get; set; }
/// <summary>
/// 密码
/// </summary>
public string? Pwd { get; set; }
}
}
- UserController.cs 授权测试 Authorize
{
/// <summary>
/// 用户
/// </summary>
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
/// <summary>
///
/// </summary>
public UserController()
{
}
/// <summary>
/// 获取用户详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}")]
[Authorize]
public async Task<IActionResult> GetAsync(int id)
{
await Task.CompletedTask;
return new JsonResult(new
{
code = 200,
msg = "success",
data = new { id = 1, account = "zhangsan", name = "张三" }
});
}
}
}
- 测试效果
Custom
- 参照SunnyFan.Author.Custom
- 添加自定义处理类
{
/// <summary>
/// 自定义Url授权处理类
/// </summary>
public class UrlTokenAuthenticationHandler : IAuthenticationHandler
{
#region 初始化
/// <summary>
///
/// </summary>
private AuthenticationScheme _authenticationScheme;//"UrltokenScheme"
/// <summary>
///
/// </summary>
private HttpContext _httpContext;
/// <summary>
///
/// </summary>
/// <param name="scheme"></param>
/// <param name="context"></param>
/// <returns></returns>
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
this._authenticationScheme = scheme;
this._httpContext = context;
return Task.CompletedTask;
}
#endregion
/// <summary>
/// 鉴权的核心方法
/// </summary>
/// <returns></returns>
public Task<AuthenticateResult> AuthenticateAsync()
{
string userInfo = this._httpContext.Request.Query["UrlToken"];//信息从哪里读
if (string.IsNullOrWhiteSpace(userInfo))
{
return Task.FromResult<AuthenticateResult>(AuthenticateResult.NoResult());
}
var claimIdentity = new ClaimsIdentity("Custom");
claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "SunnyFan"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "China"));
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimIdentity);//信息拼装和传递
return Task.FromResult<AuthenticateResult>(AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, null, _authenticationScheme.Name)));//保存到context.User
}
/// <summary>
///
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
public async Task ChallengeAsync(AuthenticationProperties? properties)
{
var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
_httpContext.Response.StatusCode = 200;
_httpContext.Response.ContentType = "application/json; charset=utf-8";
await _httpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
}
/// <summary>
///
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
public async Task ForbidAsync(AuthenticationProperties? properties)
{
var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
_httpContext.Response.StatusCode = 200;
_httpContext.Response.ContentType = "application/json; charset=utf-8";
await _httpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
}
}
/// <summary>
/// 提供个固定值
/// </summary>
public class UrlTokenAuthenticationDefaults
{
/// <summary>
/// 提供固定名称
/// </summary>
public const string AuthenticationScheme = "UrlTokenScheme";
}
}
- Program.cs 注册服务处理
builder.Services.AddAuthentication(options =>
{//配置如何鉴权
options.AddScheme<UrlTokenAuthenticationHandler>(UrlTokenAuthenticationDefaults.AuthenticationScheme, "UrlTokenScheme-Demo");
//定了个Scheme方案,key(scheme名字)-value(UrlTokenAuthenticationHandler)
options.DefaultAuthenticateScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;//不能少.默认用UrlTokenScheme
options.DefaultChallengeScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
options.DefaultForbidScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = UrlTokenAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Events.OnRedirectToLogin = async context =>
{
var result = JsonConvert.SerializeObject(new { code = 401, msg = "无权限!" });
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json; charset=utf-8";
await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(result));
};
});
app.UseRouting();
app.UseAuthentication();//鉴权中间件---表明请求过程中,需要鉴权
app.UseAuthorization();//授权中间件
- AuthorController.cs 登录模拟
{
/// <summary>
/// 授权
/// </summary>
[ApiController]
[Route("[controller]/[action]")]
public class AuthorController : ControllerBase
{
/// <summary>
///
/// </summary>
public AuthorController()
{
}
/// <summary>
/// 登录
/// </summary>
/// <param name="loginDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> LoginAsync([FromBody] LoginDto loginDto)
{
var claimIdentity = new ClaimsIdentity("Custom");
claimIdentity.AddClaim(new Claim(ClaimTypes.Name, loginDto.Account!));
claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "sunnyfancore@163.com"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
claimIdentity.AddClaim(new Claim(ClaimTypes.Country, "Chinese"));
await base.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimIdentity), new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(30),
});//登陆默认Scheme,写入Cookie
return new JsonResult(new { code = 200, msg = "success" });
}
}
/// <summary>
/// 登录对象
/// </summary>
public class LoginDto
{
/// <summary>
/// 账户
/// </summary>
public string? Account { get; set; }
/// <summary>
/// 密码
/// </summary>
public string? Pwd { get; set; }
}
}
- UserController.cs 授权测试 Authorize
{
/// <summary>
/// 用户
/// </summary>
[ApiController]
[Route("[controller]/[action]")]
public class UserController : ControllerBase
{
/// <summary>
///
/// </summary>
public UserController()
{
}
/// <summary>
/// 获取用户详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}")]
[Authorize()]
public async Task<IActionResult> GetAsync(int id)
{
var result = await base.HttpContext.AuthenticateAsync(UrlTokenAuthenticationDefaults.AuthenticationScheme);
if (result?.Principal == null)
{
return new JsonResult(new
{
code = 401,
msg = "无权限!",
});
}
await Task.CompletedTask;
return new JsonResult(new
{
code = 200,
msg = "success",
data = new { id = 1, account = "zhangsan", name = "张三" }
});
}
}
}
- 测试效果
授权
Role(角色)
//Action上标识对应的角色
[Authorize(Roles = "Admin")]
[Authorize(Roles = "User")]
Policy(策略)
- 定义policy,完成Role的功能
builder.Services.AddAuthorization(options =>
{
//自定义policy,完成Role的功能
options.AddPolicy("AdminPolicy",
policyBuilder => policyBuilder.RequireRole("Admin"));
options.AddPolicy("UserPolicy",
policyBuilder => policyBuilder.RequireRole("User"));
});
[Authorize(Roles = "Admin", Policy = "AdminPolicy")]
[Authorize(Roles = "User", Policy = "UserPolicy")]
- 追加其他自定义策略
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ComplexPolicy",
policyBuilder => policyBuilder.RequireRole("Admin")
.RequireUserName("SunnyFan")
.RequireClaim(ClaimTypes.Email));
});
[Authorize(Policy = "ComplexPolicy")]
- 自定义策略之RequireAssertion(静态条件)
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ComplexPolicy",
policyBuilder => policyBuilder.RequireRole("Admin")
.RequireUserName("SunnyFan")
.RequireClaim(ClaimTypes.Email)
.RequireAssertion(context =>//加个断言---一切都可以要求
context.User.HasClaim(c => c.Type == ClaimTypes.Role)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"
&& context.User.HasClaim(c => c.Type == ClaimTypes.Name)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Name)).Value == "SunnyFan"
&& context.User.HasClaim(c => c.Type == ClaimTypes.Email)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Email)).Value.EndsWith("@163.com")
));
});
[Authorize(Policy = "ComplexPolicy")]
- 自定义策略之AddRequirements(动态条件)
//单个叠加AddRequirements并关系(条件并)
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ComplexDynamicPolicy",
policyBuilder => policyBuilder.RequireRole("Admin")
.RequireUserName("SunnyFan")
.RequireClaim(ClaimTypes.Email)
.RequireAssertion(context =>//加个断言---一切都可以要求
context.User.HasClaim(c => c.Type == ClaimTypes.Role)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"
&& context.User.HasClaim(c => c.Type == ClaimTypes.Name)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Name)).Value == "SunnyFan"
&& context.User.HasClaim(c => c.Type == ClaimTypes.Email)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Email)).Value.EndsWith("@163.com")
)
.AddRequirements(new SingleRequirement("mail.qq.com"))
);
});
//多个叠加AddRequirements自定义并或关系
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ComplexDynamicPolicy",
policyBuilder => policyBuilder.RequireRole("Admin")
.RequireUserName("SunnyFan")
.RequireClaim(ClaimTypes.Email)
.RequireAssertion(context =>//加个断言---一切都可以要求
context.User.HasClaim(c => c.Type == ClaimTypes.Role)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"
&& context.User.HasClaim(c => c.Type == ClaimTypes.Name)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Name)).Value == "SunnyFan"
&& context.User.HasClaim(c => c.Type == ClaimTypes.Email)
&& context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Email)).Value.EndsWith("@163.com")
)
.AddRequirements(new MultiRequirement()) //控制或并关系
);
});
builder.Services.AddSingleton<IAuthorizationHandler, Mail163Handler>();
builder.Services.AddSingleton<IAuthorizationHandler, MailQQHandler>();
//使用标记
[Authorize(Roles = "Admin", Policy = "ComplexDynamicPolicy")]
AuthenticationSchemes(鉴权方案)
当系统启用多套鉴权方案时
[Authorize(AuthenticationSchemes = $"{CookieAuthenticationDefaults.AuthenticationScheme},{UrlTokenAuthenticationDefaults.AuthenticationScheme}")]
public async Task<IActionResult> UrlCookieByCookie()
{
Console.WriteLine($"主动鉴权之前:base.HttpContext.User?.Claims?.First()?.Value == null?{base.HttpContext.User?.Claims?.First()?.Value == null}");
var urlToken = await base.HttpContext.AuthenticateAsync(UrlTokenAuthenticationDefaults.AuthenticationScheme);
Console.WriteLine($"urlToken?.Principal == null ={urlToken?.Principal == null}");
var cookie = await base.HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
Console.WriteLine($"cookie?.Principal == null ={cookie?.Principal == null}");
if (urlToken?.Principal == null && cookie?.Principal == null)
{
return new JsonResult(new
{
Result = true,
Message = $"认证失败,用户未登录"
});
}
else
{
return new JsonResult(new
{
Result = true,
Message = $"认证成功,有用户信息"
});
}
}