元谋县建设局网站,微信网站地址,做网站需要好多钱,wordpress采集微信公众文章内容JWT#xff1a;全称是JSON Web Token是目前最流行的跨域身份验证、分布式登录、单点登录等解决方案。 通俗地来讲#xff0c;JWT是能代表用户身份的令牌#xff0c;可以使用JWT令牌在api接口中校验用户的身份以确认用户是否有访问api的权限。
授权#xff1a;这是使用JWT的…JWT全称是JSON Web Token是目前最流行的跨域身份验证、分布式登录、单点登录等解决方案。 通俗地来讲JWT是能代表用户身份的令牌可以使用JWT令牌在api接口中校验用户的身份以确认用户是否有访问api的权限。
授权这是使用JWT的最常见方案。一旦用户登录每个后续请求将包括JWT允许用户访问该令牌允许的路由服务和资源。 在身份验证中当用户使用其凭据成功登录时将返回JSON Web令牌。由于令牌是凭证因此必须非常小心以防止出现安全问题。一般情况下您不应该将令牌保留的时间超过要求。
每当用户想要访问受保护的路由或资源时用户代理应该使用承载模式发送JWT通常在Authorization标头中标题的内容应如下所示
Authorization: Bearer token 1、应用程序向授权服务器请求授权
2、校验用户身份校验成功返回token
3、应用程序使用访问令牌访问受保护的资源。 JWT的实现方式是将用户信息存储在客户端服务端不进行保存。每次请求都把令牌带上以校验用户登录状态这样服务就变成了无状态的服务器集群也很好扩展。
更多理论知识可以查看官网或者查看相关网友的文章如下推荐文章
asp.net core 集成JWT一https://www.cnblogs.com/7tiny/archive/2019/06/13/11012035.html五分钟带你了解啥是JWThttps://zhuanlan.zhihu.com/p/86937325C#分布式登录——jwthttps://www.cnblogs.com/yswenli/p/13510050.html 在nuget里面引用jwt集成的程序包这里需要注意的是如果你用的是.NET Core 3.1的框架的话程序包版本选择3.1.7 Microsoft.AspNetCore.Authentication.JwtBearer 添加数据访问模拟api新建控制器ValuesController
其中api/value1是可以直接访问的api/value2添加了权限校验特性标签 [Authorize]
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;namespace jwtWebAPI.Controllers
{[ApiController]public class ValuesController : ControllerBase{[HttpGet][Route(api/values1)]public ActionResultIEnumerablestring values1(){return new string[] { value1, value1 };}/*** 该接口用Authorize特性做了权限校验如果没有通过权限校验则http返回状态码为401* 调用该接口的正确姿势是* 1.登陆调用api/Auth接口获取到token* 2.调用该接口 api/value2 在请求的Header中添加参数 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiIxNTYwMzM1MzM3IiwiZXhwIjoxNTYwMzM3MTM3LCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiemhhbmdzYW4iLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAifQ.1S-40SrA4po2l4lB_QdzON_G5ZNT4P_6U25xhTcl7hI* Bearer后面有空格且后面是第一步中接口返回的token值* */[HttpGet][Route(api/value2)][Authorize]public ActionResultIEnumerablestring value2(){//这是获取自定义参数的方法var auth HttpContext.AuthenticateAsync().Result.Principal.Claims;var userName auth.FirstOrDefault(t t.Type.Equals(ClaimTypes.NameIdentifier))?.Value;return new string[] { 访问成功这个接口登陆过的用户都可以访问, $userName{userName} };}}
}
添加模拟登陆生成Token的api新建控制器AuthController 这里模拟一下登陆校验只验证了用户密码不为空即通过校验真实环境完善校验用户和密码的逻辑。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;namespace jwtWebAPI.Controllers
{[ApiController]public class AuthController : Controller{/// summary/// 通过账号密码获取Token/// /summary/// param nameuserName/param/// param namepwd/param/// returnsToken/returns[AllowAnonymous][HttpGet][Route(api/auth)]public IActionResult GetToken(string userName, string pwd){if (!string.IsNullOrEmpty(userName)){//每次登陆动态刷新Const.ValidAudience userName pwd DateTime.Now.ToString();// push the user’s name into a claim, so we can identify the user later on.//这里可以随意加入自定义的参数key可以自己随便起var claims new[]{new Claim(JwtRegisteredClaimNames.Nbf,${new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}) ,new Claim (JwtRegisteredClaimNames.Exp,${new DateTimeOffset(DateTime.Now.AddMinutes(3)).ToUnixTimeSeconds()}),new Claim(ClaimTypes.NameIdentifier, userName)};//sign the token using a secret key.This secret will be shared between your API and anything that needs to check that the token is legit.var key new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey));var creds new SigningCredentials(key, SecurityAlgorithms.HmacSha256);//.NET Core’s JwtSecurityToken class takes on the heavy lifting and actually creates the token.var token new JwtSecurityToken(//颁发者issuer: Const.Domain,//接收者audience: Const.ValidAudience,//过期时间可自行设定注意和上面的claims内部Exp参数保持一致expires: DateTime.Now.AddMinutes(3),//签名证书signingCredentials: creds,//自定义参数claims: claims);return Ok(new{token new JwtSecurityTokenHandler().WriteToken(token)});}else{return BadRequest(new { message username or password is incorrect. });}}}
}
Startup添加JWT验证的相关配置
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace jwtWebAPI
{public class Startup{public Startup(IConfiguration configuration){Configuration configuration;}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){//添加jwt验证services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options {options.TokenValidationParameters new TokenValidationParameters{ValidateLifetime true,//是否验证失效时间ClockSkew TimeSpan.FromSeconds(30), //时间偏移量允许误差时间ValidateAudience true,//是否验证Audience验证之前的token是否失效//ValidAudience Const.GetValidudience(),//Audience//这里采用动态验证的方式在重新登陆时刷新token旧token就强制失效了AudienceValidator (m, n, z) {return m ! null m.FirstOrDefault().Equals(Const.ValidAudience);},ValidateIssuer true,//是否验证Issuer颁发者ValidAudience Const.Domain,//Audience 【Const是新建的一个常量类】 接收者 ValidIssuer Const.Domain,//Issuer这两项和前面签发jwt的设置一致 颁发者ValidateIssuerSigningKey true,//是否验证SecurityKeyIssuerSigningKey new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到秘钥SecurityKey};options.Events new JwtBearerEvents{OnAuthenticationFailed context {//Token expiredif (context.Exception.GetType() typeof(SecurityTokenExpiredException)){context.Response.Headers.Add(Token-Expired, true);}return Task.CompletedTask;}};});services.AddControllers();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IWebHostEnvironment env){ //添加jwt验证app.UseAuthentication();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseHttpsRedirection();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints {endpoints.MapControllers();});}}
}
创建常量类Const
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;namespace jwtWebAPI
{public class Const{/// summary/// 这里为了演示写死一个密钥。实际生产环境可以从配置文件读取,这个是用网上工具随便生成的一个密钥md5或者其他都可以/// /summarypublic const string SecurityKey 48754F4C58F9EA428FE09D714E468211;/// summary/// 站点地址颁发者、接受者这里测试和当前本地运行网站相同实际发到正式环境应为域名地址/// /summarypublic const string Domain https://localhost:44345;/// summary/// 受理人之所以弄成可变的是为了用接口动态更改这个值以模拟强制Token失效/// 真实业务场景可以在数据库或者redis存一个和用户id相关的值生成token和验证token的时候获取到持久化的值去校验/// 如果重新登陆则刷新这个值/// /summarypublic static string ValidAudience;}
} JWT登录授权测试成功 返回了状态码401也就是未经授权:访问由于凭据无效被拒绝。 说明JWT校验生效了我们的接口收到了保护。
调用模拟登陆授权接口https://localhost:44345/api/auth?userNamexiongzepwd123456
这里的用户密码是随便写的因为我们模拟登陆只是校验了下非空因此写什么都能通过。 然后我们得到了一个xxx.yyy.zzz 格式的 token 值。我们把token复制出来。
在刚才401的接口https://localhost:44345/api/values2请求header中添加JWT的参数把我们的token加上去
再次调用我们的模拟数据接口但是这次我们加了一个headerKEYAuthorization ValueBearer Tokne的值
这里需要注意 Bearer 后面是有一个空格的然后就是我们上一步获取到的token 得到返回值正确授权成功我们是支持自定义返回参数的上面代码里面有相关内容比如用户名这些不敏感的信息可以带着返回。
等token设置的过期时间到了或者重新生成了新的Token没有及时更新那么我们的授权也到期401 升级操作接口权限隔离
上面的操作是所有登录授权成功的角色都可以进行调用所有接口那么我们现在想要进行接口隔离限制
也就是说虽然授权登录了但是我这个接口是指定权限访问的。
比如说删除接口只能管理员角色操作那么其他角色虽然授权登录了但是没有权限调用删除接口。
我们在原来的操作进行改造升级看一下。
添加类
新建一个AuthManagement文件夹添加PolicyRequirement类和PolicyHandler类
PolicyRequirement类
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;namespace jwtWebAPI.AuthManagement
{/// summary/// 权限承载实体/// /summarypublic class PolicyRequirement : IAuthorizationRequirement{/// summary/// 用户权限集合/// /summarypublic ListUserPermission UserPermissions { get; private set; }/// summary/// 无权限action/// /summarypublic string DeniedAction { get; set; }/// summary/// 构造/// /summarypublic PolicyRequirement(){//没有权限则跳转到这个路由DeniedAction new PathString(/api/nopermission);//用户有权限访问的路由配置,当然可以从数据库获取UserPermissions new ListUserPermission {new UserPermission { Url/api/values3, UserNameadmin},};}}/// summary/// 用户权限承载实体/// /summarypublic class UserPermission{/// summary/// 用户名/// /summarypublic string UserName { get; set; }/// summary/// 请求Url/// /summarypublic string Url { get; set; }}
}
PolicyHandler类(注意2.x和3.x的区别)
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;namespace jwtWebAPI.AuthManagement
{public class PolicyHandler : AuthorizationHandlerPolicyRequirement{private readonly IHttpContextAccessor _httpContextAccessor;public PolicyHandler(IHttpContextAccessor httpContextAccessor){_httpContextAccessor httpContextAccessor;}protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement){//赋值用户权限var userPermissions requirement.UserPermissions;var httpContext _httpContextAccessor.HttpContext;//请求Urlvar questUrl httpContext.Request.Path.Value.ToUpperInvariant();//是否经过验证var isAuthenticated httpContext.User.Identity.IsAuthenticated;if (isAuthenticated){if (userPermissions.GroupBy(g g.Url).Any(w w.Key.ToUpperInvariant() questUrl)){//用户名var userName httpContext.User.Claims.SingleOrDefault(s s.Type ClaimTypes.NameIdentifier).Value;if (userPermissions.Any(w w.UserName userName w.Url.ToUpperInvariant() questUrl)){context.Succeed(requirement);}else{无权限跳转到拒绝页面//httpContext.Response.Redirect(requirement.DeniedAction);return Task.CompletedTask;}}else{context.Succeed(requirement);}}return Task.CompletedTask;}}
}
添加指定角色
在 AuthController 控制器的GetToken授权加入自定义的参数如下
new Claim(Role, userName) //这里是角色我使用登录账号admin代替 在 AuthController 控制器里面添加无权限访问的方法
[AllowAnonymous]
[HttpGet]
[Route(api/nopermission)]
public IActionResult NoPermission()
{return Forbid(No Permission!);
}
修改Startup配置
在startup.cs的ConfigureServices 方法里面添加策略鉴权模式、添加JWT Scheme、注入授权Handler
修改后的文件如下
using jwtWebAPI.AuthManagement;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace jwtWebAPI
{public class Startup{public Startup(IConfiguration configuration){Configuration configuration;}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){services//添加策略鉴权模式.AddAuthorization(options {options.AddPolicy(Permission, policy policy.Requirements.Add(new PolicyRequirement()));})//添加JWT Scheme.AddAuthentication(s {s.DefaultAuthenticateScheme JwtBearerDefaults.AuthenticationScheme;s.DefaultScheme JwtBearerDefaults.AuthenticationScheme;s.DefaultChallengeScheme JwtBearerDefaults.AuthenticationScheme;})//添加jwt验证.AddJwtBearer(options {options.TokenValidationParameters new TokenValidationParameters{ValidateLifetime true,//是否验证失效时间ClockSkew TimeSpan.FromSeconds(30), //时间偏移量允许误差时间ValidateAudience true,//是否验证Audience验证之前的token是否失效//ValidAudience Const.GetValidudience(),//Audience//这里采用动态验证的方式在重新登陆时刷新token旧token就强制失效了AudienceValidator (m, n, z) {return m ! null m.FirstOrDefault().Equals(Const.ValidAudience);},ValidateIssuer true,//是否验证Issuer颁发者ValidAudience Const.Domain,//Audience 【Const是新建的一个常量类】 接收者 ValidIssuer Const.Domain,//Issuer这两项和前面签发jwt的设置一致 颁发者ValidateIssuerSigningKey true,//是否验证SecurityKeyIssuerSigningKey new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到秘钥SecurityKey};options.Events new JwtBearerEvents{OnAuthenticationFailed context {//Token expiredif (context.Exception.GetType() typeof(SecurityTokenExpiredException)){context.Response.Headers.Add(Token-Expired, true);}return Task.CompletedTask;}};});//注入授权Handlerservices.AddSingletonIAuthorizationHandler, PolicyHandler();//注入获取HttpContextservices.TryAddSingletonIHttpContextAccessor, HttpContextAccessor();services.AddControllers();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IWebHostEnvironment env){ //添加jwt验证app.UseAuthentication();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseHttpsRedirection();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints {endpoints.MapControllers();});}}
}
添加api访问的方法
在 ValuesController控制器添加指定权限访问的方法如下
/*** 这个接口必须用admin**/[HttpGet][Route(api/values3)][Authorize(Permission)]public ActionResultIEnumerablestring values3(){//这是获取自定义参数的方法var auth HttpContext.AuthenticateAsync().Result.Principal.Claims;var userName auth.FirstOrDefault(t t.Type.Equals(ClaimTypes.NameIdentifier))?.Value;var role auth.FirstOrDefault(t t.Type.Equals(Role))?.Value;return new string[] { 访问成功这个接口有管理员权限才可以访问, $userName{userName}, $Role{role} };} 不同权限测试访问
我们同样的方法去模拟登录https://localhost:44345/api/auth?userNamexiongzepwd123
注意账号先不用admin登录然后用返回的token去请求我们刚刚添加的指定权限访问的接口这个时候是没有权限访问的因为这个是admin权限访问。
我们同样的方法去模拟登录https://localhost:44345/api/auth?userNamexiongzepwd123
注意账号先不用admin登录然后用返回的token去请求我们刚刚添加的指定权限访问的接口这个时候是没有权限访问的因为这个是admin权限访问。 我们同样的方法去模拟登录https://localhost:44345/api/auth?userNameadminpwd123
访问成功。 完结。。。