asp.net core 2.0 web api基于JWT自定义策略授权
JWT(json web token)是一种基于json的身份验证机制,流程如下:
通过登录,来获取Token,再在之后每次请求的Header中追加Authorization为Token的凭据,服务端验证通过即可能获取想要访问的资源。关于JWT的技术,可参考网络上文章,这里不作详细说明,这篇博文,主要说明在asp.net core 2.0中,基于jwt的web api的权限设置,即在asp.net core中怎么用JWT,再次就是不同用户或角色因为权限问题,即使援用Token,也不能访问不该访问的资源。基本思路是我们自定义一个策略,来验证用户,和验证用户授权,PermissionRequirement是验证传输授权的参数。在Startup的ConfigureServices注入验证(Authentication),授权(Authorization),和JWT(JwtBearer)自定义策略:已封闭成AuthorizeRolicy.JWT nuget包,并发布到nuget上:https://www.nuget.org/packages/AuthorizePolicy.JWT/源码如下:JwtToken.cs
claims, PermissionRequirement permissionRequirement) { var now = DateTime.UtcNow; var jwt = new JwtSecurityToken( issuer: permissionRequirement.Issuer, audience: permissionRequirement.Audience, claims: claims, notBefore: now, expires: now.Add(permissionRequirement.Expiration), signingCredentials: permissionRequirement.SigningCredentials ); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); var response = new { Status = true, access_token = encodedJwt, expires_in = permissionRequirement.Expiration.TotalMilliseconds, token_type = "Bearer" }; return response; }
PermissionRequirement.cs
[code=c#;toolbar:false"> /// <summary> /// 必要参数类 /// </summary> public class PermissionRequirement : IAuthorizationRequirement { /// <summary> /// 用户权限集合 /// </summary> public List<Permission> Permissions { get; private set; } /// <summary> /// 无权限action /// </summary> public string DeniedAction { get; set; } /// <summary> /// 认证授权类型 /// </summary> public string ClaimType { internal get; set; } /// <summary> /// 请求路径 /// </summary> public string LoginPath { get; set; } = "/Api/Login"; /// <summary> /// 发行人 /// </summary> public string Issuer { get; set; } /// <summary> /// 订阅人 /// </summary> public string Audience { get; set; } /// <summary> /// 过期时间 /// </summary> public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5000); /// <summary> /// 签名验证 /// </summary> public SigningCredentials SigningCredentials { get; set; } /// <summary> /// 构造 /// </summary> /// <param name="deniedAction">无权限action</param> /// <param name="userPermissions">用户权限集合</param> /// <summary> /// 构造 /// </summary> /// <param name="deniedAction">拒约请求的url</param> /// <param name="permissions">权限集合</param> /// <param name="claimType">声明类型</param> /// <param name="issuer">发行人</param> /// <param name="audience">订阅人</param> /// <param name="signingCredentials">签名验证实体</param> public PermissionRequirement(string deniedAction, List<Permission> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials) { ClaimType = claimType; DeniedAction = deniedAction; Permissions = permissions; Issuer = issuer; Audience = audience; SigningCredentials = signingCredentials; } }PermissionHandler.cs /// <summary>
/// 权限授权Handler
/// </summary>
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
/// <summary>
/// 验证方案提供对象
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; set; }
/// <summary>
/// 自定义策略参数
/// </summary>
public PermissionRequirement Requirement
{ get; set; }
/// <summary>
/// 构造
/// </summary>
/// <param name="schemes"></param>
public PermissionHandler(IAuthenticationSchemeProvider schemes)
{
Schemes = schemes;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
////赋值用户权限
Requirement = requirement;
//从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;
//请求Url
var questUrl = httpContext.Request.Path.Value.ToLower();
//判断请求是否停止
var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
context.Fail();
return;
}
}
//判断请求是否拥有凭据,即有没有登录
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
//result?.Principal不为空即登录成功
if (result?.Principal != null)
{
httpContext.User = result.Principal;
//权限中是否存在请求的url
if (Requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
{
var name = httpContext.User.Claims.SingleOrDefault(s => s.Type == requirement.Cl
20000
aimType).Value;
//验证权限
if (Requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() <= 0)
{
//无权限跳转到拒绝页面
httpContext.Response.Redirect(requirement.DeniedAction);
}
}
context.Succeed(requirement);
return;
}
}
//判断没有登录时,是否访问登录的url,并且是Post请求,并助是form表单提交类型,否则为失败
if (!questUrl.Equals(Requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST")
|| !httpContext.Request.HasFormContentType))
{
context.Fail();
return;
}
context.Succeed(requirement);
}
}
新建asp.net core 2.0的web api项目,并在项目添加AuthorizePolicy.JWT如图
先设置配置文件,用户可以定义密匙和发生人,订阅人 "Audience": { "Secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", "Issuer": "gsw", "Audience": "everone" }在ConfigureServices中注入验证(Authentication),授权(Authorization),和JWT(JwtBearer)Startup.cs
[code=c#;toolbar:false"> public void ConfigureServices(IServiceCollection services) { //读取配置文件 var audienceConfig = Configuration.GetSection("Audience"); var symmetricKeyAsBase64 = audienceConfig["Secret"]; var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ValidateIssuer = true, ValidIssuer = audienceConfig["Issuer"], ValidateAudience = true, ValidAudience = audienceConfig["Audience"], ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); services.AddAuthorization(options => { //这个集合模拟用户权限表,可从数据库中查询出来 var permission = new List<Permission> { new Permission { Url="/", Name="admin"}, new Permission { Url="/api/values", Name="admin"}, new Permission { Url="/", Name="system"}, new Permission { Url="/api/values1", Name="system"} }; //如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名 var permissionRequirement = new PermissionRequirement("/api/denied", permission, ClaimTypes.Role, audienceConfig["Issuer"], audienceConfig["Audience"], signingCredentials); options.AddPolicy("Permission", policy => policy.Requirements.Add(permissionRequirement)); }).AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { //不使用https o.RequireHttpsMetadata = false; o.TokenValidationParameters = tokenValidationParameters; }); //注入授权Handler services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); services.AddMvc(); }下面定义一个控制台(.NetFramewrok)程序,用RestSharp来访问我们定义的web api,其中1为admin角色登录,2为system角色登录,3为错误用户密码登录,4是一个查询功能,在startup.cs中,admin角色是具有查询/api/values的权限的,所以用admin登录是能正常访问的,用system登录,能成功登录,但没有权限访问/api/values,用户名密码错误,访问/aip/values,直接是没有授权的 class Program { /// <summary> /// 访问Url /// </summary> static string _url = "http://localhost:39286"; static void Main(string[] args) { dynamic token = null; while (true) { Console.WriteLine("1、登录【admin】 2、登录【system】 3、登录【错误用户名密码】 4、查询数据 "); var mark = Console.ReadLine(); var stopwatch = new Stopwatch(); stopwatch.Start(); switch (mark) { case "1": token = AdminLogin(); break; case "2": token = SystemLogin(); break; case "3": token = NullLogin(); break; case "4": AdminInvock(token); break; } stopwatch.Stop(); TimeSpan timespan = stopwatch.Elapsed; Console.WriteLine($"间隔时间:{timespan.TotalSeconds}"); } } static dynamic NullLogin() { var loginClient = new RestClient(_url); var loginRequest = new RestRequest("/api/login", Method.POST); loginRequest.AddParameter("username", "gswaa"); loginRequest.AddParameter("password", "111111"); //或用用户名密码查询对应角色 loginRequest.AddParameter("role", "system"); IRestResponse loginResponse = loginClient.Execute(loginRequest); var loginContent = loginResponse.Content; Console.WriteLine(loginContent); return Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent); } static dynamic SystemLogin() { var loginClient = new RestClient(_url); var loginRequest = new RestRequest("/api/login", Method.POST); loginRequest.AddParameter("username", "gsw"); loginRequest.AddParameter("password", "111111"); //或用用户名密码查询对应角色 loginRequest.AddParameter("role", "system"); IRestResponse loginResponse = loginClient.Execute(loginRequest); var loginContent = loginResponse.Content; Console.WriteLine(loginContent); return Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent); } static dynamic AdminLogin() { var loginClient = new RestClient(_url); var loginRequest = new RestRequest("/api/login", Method.POST); loginRequest.AddParameter("username", "gsw"); loginRequest.AddParameter("password", "111111"); //或用用户名密码查询对应角色 loginRequest.AddParameter("role", "admin"); IRestResponse loginResponse = loginClient.Execute(loginRequest); var loginContent = loginResponse.Content; Console.WriteLine(loginContent); return Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent); } static void AdminInvock(dynamic token) { var client = new RestClient(_url); //这里要在获取的令牌字符串前加Bearer string tk = "Bearer " + Convert.ToString(token?.access_token); client.AddDefaultHeader("Authorization", tk); var request = new RestRequest("/api/values", Method.GET); IRestResponse response = client.Execute(request); var content = response.Content; Console.WriteLine($"状态:{response.StatusCode} 返回结果:{content}"); } } 运行结果: 源码:https://github.com/axzxs2001/AuthorizePolicy.JWT
- asp.net core 2.0 web api基于JWT自定义策略授权
- 基于JWT(Json Web Token)的ASP.NET Web API授权方式
- asp.net core 2.0 web api基于JWT自定义策略授权
- asp.net core 2.0 web api基于JWT自定义策略授权
- 5.1基于JWT的认证和授权「深入浅出ASP.NET Core系列」
- Asp.Net Core 2.0 项目实战(9) 日志记录,基于Nlog或Microsoft.Extensions.Logging的实现及调用实例
- 【转载】asp.net core 2.0的认证和授权
- [Asp.Net web api]基于自定义Filter的安全认证
- Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录
- asp.net core webapi实现jwt授权认证
- asp.net core 自定义401和异常显示内容(JWT认证、Cookie Base认证失败显示内容)
- [译]Scott Mitchell 的ASP.NET 2.0数据教程之十一: 基于数据的自定义格式化
- 从头编写 asp.net core 2.0 web api 基础框架 (1)
- 基于 ASP.NET Core 2.1 的 Razor Class Library 实现自定义错误页面的公用类库
- Asp.Net Core 2.0 项目实战(11) 基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级
- Scott Mitchell 的ASP.NET 2.0数据教程之十一: 基于数据的自定义格式化
- Asp .Net Core 2.0 登录授权以及多用户登录
- ASP.NET Core 2.1中基于角色的授权
- ASP.NET Core 认证与授权[6]:授权策略是怎么执行的?
- asp.net core 2.0的认证和授权