6月15日,在端午节前的最后一个工作日,想起有段日子没有写过文章了,倒有些荒疏了。今借夏日蒸蒸之气,偷得浮生半日悠闲。闲话就说到这里吧,提前祝大家端午愉快(屈原听了该不高兴了:))!
.NetCore自发布以来,颇受关注,现在.Net Core2.0已经正式发布,边迫不及待的将.Net跨平台移植的工作进行到底。想来,也费不了多少事儿。我经常和同事们说,要敢于尝试新鲜事物,不阴损守旧,方能使自己不断进步,站在队伍的前列。下面就关于Asp.Net Core在Web 及API项目上身份认证的问题做下简单的阐述。
一、Asp.Net Core Web项目的登录认证
在MVC Web项目中,做用户登录授权,是必不可少的工作,不知道大家平时是怎么做的,我想,大多朋友还是使用微软提供的一套认证机制,可以省去很多功夫。从WebForm时代的Form身份认证,无非是通过客户端Cookie中存储认证票据,在请求受保护的资源时,通过Cookie中携带的身份票据,再有Asp.net的认证模块,完整对请求者的身份认证。这一过程,是很清晰简单的了。在MVC中,大多是通过中间件(MiddleWare)来完整认证授权过程。在ASP.NETMVC中,我们了解到基于声明的授权认证(Claim),这种认证方式,好处在于,我们想在用户授权时,存储多个属性信息,只需要添加多个声明即可,我们在微软的认证中间件中,看到的都是定义好的常量,当然,我们可以定义自己的ClaimTypes。然我们看看微软在.NetCore中定义的一些声明吧:
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | // // Summary: // http://schemas.xmlsoap.org/ws/2009/09/identity/claims/actor. public const string Actor = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims/actor" ; // // Summary: // The URI for a claim that specifies the postal code of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode. public const string PostalCode = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode" ; // // Summary: // The URI for a claim that specifies the primary group SID of an entity, http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid. public const string PrimaryGroupSid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid" ; // // Summary: // The URI for a claim that specifies the primary SID of an entity, http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid. public const string PrimarySid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid" ; // // Summary: // The URI for a claim that specifies the role of an entity, http://schemas.microsoft.com/ws/2008/06/identity/claims/role. public const string Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" ; // // Summary: // The URI for a claim that specifies an RSA key, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/rsa. public const string Rsa = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/rsa" ; // // Summary: // The URI for a claim that specifies a serial number, http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber. public const string SerialNumber = "http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber" ; // // Summary: // The URI for a claim that specifies a security identifier (SID), http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid. public const string Sid = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid" ; // // Summary: // The URI for a claim that specifies a service principal name (SPN) claim, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/spn. public const string Spn = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/spn" ; // // Summary: // The URI for a claim that specifies the state or province in which an entity resides, // http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince. public const string StateOrProvince = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince" ; // // Summary: // The URI for a claim that specifies the street address of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress. public const string StreetAddress = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress" ; // // Summary: // The URI for a claim that specifies the surname of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname. public const string Surname = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" ; // // Summary: // The URI for a claim that identifies the system entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system. public const string System = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" ; // // Summary: // The URI for a claim that specifies a thumbprint, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/thumbprint. // A thumbprint is a globally unique SHA-1 hash of an X.509 certificate. public const string Thumbprint = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/thumbprint" ; // // Summary: // The URI for a claim that specifies a user principal name (UPN), http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn. public const string Upn = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" ; // // Summary: // The URI for a claim that specifies a URI, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri. public const string Uri = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata. public const string UserData = "http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/version. public const string Version = "http://schemas.microsoft.com/ws/2008/06/identity/claims/version" ; // // Summary: // The URI for a claim that specifies the webpage of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage. public const string Webpage = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage" ; // // Summary: // The URI for a claim that specifies the Windows domain account name of an entity, // http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname. public const string WindowsAccountName = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdeviceclaim. public const string WindowsDeviceClaim = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdeviceclaim" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdevicegroup. public const string WindowsDeviceGroup = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsdevicegroup" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsfqbnversion. public const string WindowsFqbnVersion = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsfqbnversion" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/windowssubauthority. public const string WindowsSubAuthority = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowssubauthority" ; // // Summary: // The URI for a claim that specifies the alternative phone number of an entity, // http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone. public const string OtherPhone = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone" ; // // Summary: // The URI for a claim that specifies the name of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier. public const string NameIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" ; // // Summary: // The URI for a claim that specifies the name of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name. public const string Name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" ; // // Summary: // The URI for a claim that specifies the mobile phone number of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone. public const string MobilePhone = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone" ; // // Summary: // The URI for a claim that specifies the anonymous user; http://schemas.xmlsoap.org/ws/2005/05/identity/claims/anonymous. public const string Anonymous = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/anonymous" ; // // Summary: // The URI for a claim that specifies details about whether an identity is authenticated, // http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authenticated. public const string Authentication = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authentication" ; // // Summary: // The URI for a claim that specifies the instant at which an entity was authenticated; // http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant. public const string AuthenticationInstant = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant" ; // // Summary: // The URI for a claim that specifies the method with which an entity was authenticated; // http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod. public const string AuthenticationMethod = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod" ; // // Summary: // The URI for a claim that specifies an authorization decision on an entity; http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authorizationdecision. public const string AuthorizationDecision = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authorizationdecision" ; // // Summary: // The URI for a claim that specifies the cookie path; http://schemas.microsoft.com/ws/2008/06/identity/claims/cookiepath. public const string CookiePath = "http://schemas.microsoft.com/ws/2008/06/identity/claims/cookiepath" ; // // Summary: // The URI for a claim that specifies the country/region in which an entity resides, // http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country. public const string Country = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country" ; // // Summary: // The URI for a claim that specifies the date of birth of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth. public const string DateOfBirth = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth" ; // // Summary: // The URI for a claim that specifies the deny-only primary group SID on an entity; // http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid. // A deny-only SID denies the specified entity to a securable object. public const string DenyOnlyPrimaryGroupSid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid" ; // // Summary: // The URI for a claim that specifies the deny-only primary SID on an entity; http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid. // A deny-only SID denies the specified entity to a securable object. public const string DenyOnlyPrimarySid = "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid" ; // // Summary: // The URI for a claim that specifies a deny-only security identifier (SID) for // an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid. // A deny-only SID denies the specified entity to a securable object. public const string DenyOnlySid = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsuserclaim. public const string WindowsUserClaim = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsuserclaim" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlywindowsdevicegroup. public const string DenyOnlyWindowsDeviceGroup = "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlywindowsdevicegroup" ; // // Summary: // http://schemas.microsoft.com/ws/2008/06/identity/claims/dsa. public const string Dsa = "http://schemas.microsoft.com/ws/2008/06/identity/claims/dsa" ; // // Summary: // The URI for a claim that specifies the email address of an entity, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email. public const string Email = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" ; |
那么我们在Asp.Net Core项目中的认证,也是比较简单的。也是通过HttpContext的扩展方法SignInAsync,来传入声明的身份信息。要使用的微软的认证组件,我们在.Net Core Web项目中,做如下改动:
首先,在Start.cs类中,添加服务,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /// <summary> /// /// </summary> /// <param name="services"></param> public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o => { o.Cookie.Name = "_AdminTicketCookie" ; o.LoginPath = new PathString( "/Account/Login" ); o.LogoutPath = new PathString( "/Account/Login" ); o.AccessDeniedPath = new PathString( "/Error/Forbidden" ); }); services.AddTransient<TiKu.Application.Interfaces.IAdminService, TiKu.Application.AdminService>(); services.AddMvc(); } |
其次,添加认证中间件
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 | /// <summary> /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. /// </summary> /// <param name="app"></param> /// <param name="env"></param> public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler( "/Home/Error" ); } app.UseStaticFiles(); app.UseAuthentication(); //添加认证中间件 app.UseMvc(routes => { routes.MapRoute( name: "default" , template: "{controller=Home}/{action=Index}/{id?}" ); }); } |
最后,在用户登录的地方,登录成功后,调用HttpContext的SignIn方法,将授权信息写入Cookie,示例代码如下:
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 | /// <summary> /// <![CDATA[登陆]]> /// </summary> /// <param name="model"></param> /// <returns></returns> [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(Models.LoginViewModel model) { try { //模型验证通过后 if (ModelState.IsValid) { model.password = TiKu.Common.Security.MD5.Md5(model.password); //MD5加密 TiKu.Domain.Entity.tb_Admin admin = await _AdminService.CheckAccountAndPassword(account: model.account, password: model.password); //验证用户名密码 if (admin != null ) { var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); //一定要声明AuthenticationScheme identity.AddClaim( new Claim(ClaimTypes.Name, admin.Account)); identity.AddClaim( new Claim(ClaimTypes.NameIdentifier, admin.Id.ToString())); await HttpContext.SignInAsync(identity.AuthenticationType, new ClaimsPrincipal(identity), new AuthenticationProperties { IsPersistent = model.isPersistent, RedirectUri = "/Home/Index" , ExpiresUtc = new System.DateTimeOffset(dateTime: DateTime.Now.AddHours(6)), }); //更新登陆时间 await _AdminService.UpdateLastLoginTime(id: admin.Id); } else { await HttpContext.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme); ModelState.AddModelError( "" , "用户名或密码错误!" ); } } } catch (Exception ex) { ModelState.AddModelError( "" , "用户名或密码错误!" ); _Logger.Error( "用户登录时发生错误!" , ex); } return View(model); } |
这样就完成了Asp.net core web项目的登录认证工作。
二、Asp.Net Core WebApi基于JWT的认证授权
关于JWT的工作原理,大家可以自行了解(https://jwt.io/)。JWT实现了服务端无状态,在分布式服务,会话一致性,单点登录等方面,凸显优势,不占用服务端资源。使用JWT需要注意的是,令牌过期后刷新,以及更改密码后令牌未过期的处理问题。
这里,我以JWT作为.net core webapi项目的认证方式。
首先,我再Api项目中新建了一个名为OAuthController的控制器,定义一个Action名为Token的方法,用来让客户端获取令牌之用,具体代码如下:
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 | /// <summary> /// <![CDATA[获取访问令牌]]> /// </summary> /// <param name="user"></param> /// <param name="password"></param> /// <returns></returns> [HttpPost] public async Task<TiKu.Domain.ValueObject.RestfulData<TiKu.Domain.ValueObject.AccessTokenObj>> Token( string user, string password) { var result = new TiKu.Domain.ValueObject.RestfulData<TiKu.Domain.ValueObject.AccessTokenObj>(); try { if ( string .IsNullOrEmpty(user)) throw new ArgumentNullException( "user" , "用户名不能为空!" ); if ( string .IsNullOrEmpty(password)) throw new ArgumentNullException( "password" , "密码不能为空!" ); //验证用户名和密码 var userInfo = await _UserService.CheckUserAndPassword(mobile: user, password: password); var claims = new Claim[] { new Claim(ClaimTypes.Name,user), new Claim(ClaimTypes.NameIdentifier,userInfo.Id.ToString()), }; var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration[ "JwtSecurityKey" ])); var expires = DateTime.Now.AddDays(28); // var token = new JwtSecurityToken( issuer: Configuration[ "issuer" ], audience: Configuration[ "audience" ], claims: claims, notBefore: DateTime.Now, expires: expires, signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)); //生成Token string jwtToken = new JwtSecurityTokenHandler().WriteToken(token); result.code = 1; result.data = new Domain.ValueObject.AccessTokenObj() { AccessToken = jwtToken, Expires = TiKu.Common.Utility.Util.ToUnixTime(expires) }; result.message = "授权成功!" ; return result; } catch (Exception ex) { result.message = ex.Message; result.code = 0; logger.Error( "获取访问令牌时发生错误!" , ex); return result; } } |
这里,我定义了一个统一返回数据格式的模型-RestfulData,其中有不返回数据data的RestfulData和带data数据的RestfulData<T>,以及返回集合类型的RestfulArray<T>,具体代码如下:
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 | /// <summary> /// /// </summary> public class RestfulData { /// <summary> /// <![CDATA[错误码]]> /// </summary> public int code { get ; set ; } /// <summary> ///<![CDATA[消息]]> /// </summary> public string message { get ; set ; } /// <summary> /// <![CDATA[相关的链接帮助地址]]> /// </summary> public string url { get ; set ; } } /// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> public class RestfulData<T> : RestfulData { /// <summary> /// <![CDATA[数据]]> /// </summary> public virtual T data { get ; set ; } } /// <summary> /// <![CDATA[返回数组]]> /// </summary> /// <typeparam name="T"></typeparam> public class RestfulArray<T> : ResultData<IEnumerable<T>> { } |
配置JWT认证服务,在Start.cs启动类中,配置如下:
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 | /// <summary> /// /// </summary> /// <param name="services"></param> public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddSingleton<IConfiguration>(Configuration); services.AddMemoryCache(); //添加基于内存的缓存支持 services.AddAutofac(); //配置授权 services.AddAuthentication(options => { options.DefaultAuthenticateScheme = "JwtBearer" ; options.DefaultChallengeScheme = "JwtBearer" ; }).AddJwtBearer( "JwtBearer" , (jwtBearerOptions) => { jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true , IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration[ "JwtSecurityKey" ])), //秘钥 ValidateIssuer = true , ValidIssuer = Configuration[ "issuer" ], ValidateAudience = true , ValidAudience = Configuration[ "audience" ], ValidateLifetime = true , ClockSkew = TimeSpan.FromMinutes(5) }; }); services.AddMvc(); //IOC Autofac var builder = new ContainerBuilder(); builder.Populate(services); //注册应用服务 var assemblyApplicationService = System.Reflection.Assembly.Load( "TiKu.Application" ); builder.RegisterAssemblyTypes(assemblyApplicationService).AsImplementedInterfaces(); var container = builder.Build(); Container = container; return new AutofacServiceProvider(container); } |
上面使用了IOC容器Autofac。
其次,配置认证中间件:
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 | /// <summary> /// /// </summary> /// <param name="app"></param> /// <param name="env"></param> public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseAuthentication(); //配置授权 //处理异常 app.UseStatusCodePages( new StatusCodePagesOptions() { HandleAsync = (context) => { if (context.HttpContext.Response.StatusCode == 401) { using (System.IO.StreamWriter sw = new System.IO.StreamWriter(context.HttpContext.Response.Body)) { sw.Write(Newtonsoft.Json.JsonConvert.SerializeObject( new { status = 401, message = "access denied!" , })); } } return System.Threading.Tasks.Task.Delay(0); } }); app.UseMvc(routes => { routes.MapRoute(name: "default" , template: "api/{controller=Home}/{action=Index}/{id?}" ); routes.MapRoute(name: "mvc" , template: "{controller=Home}/{action=Index}/{id?}" ); }); } |
为了测试,我们给ValuesController控制器添加Authorize特性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /// <summary> /// /// </summary> [Authorize] [Route( "api/[controller]" )] public class ValuesController : Controller { // GET api/values [HttpGet] public IEnumerable< string > Get() { return new string [] { "value1" , "value2" }; } }<em id= "__mceDel" style= "background-color: rgba(255, 255, 255, 1); font-family: "PingFang SC", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px" > </em> |
最后,让我们测试下API的授权,这里,我以Ajax模拟API的调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | script type= "text/javascript" > //获取令牌 $.post( "/oauth/token" , $.param({ user: "lichaoqiang" , password: "fdsfds" })).done( function (data) { if (data.code === 1) { localStorage.setItem( "token" , data.data.accessToken); } }); //设置HTTP头 $.ajaxSetup({ beforeSend: function (xhr) { if (localStorage.getItem( "token" ) !== null ) { xhr.setRequestHeader( 'Authorization' , 'Bearer ' + localStorage.getItem( "token" )); } } }); $.getJSON( "/api/values" , function (data) { console.log(data); }); //获取受保护的资源 </script> |
看下效果,直接访问/api/values,会出现如下图:
当客户请求受保护的资源时,通过HTTP header携带上token。这里需要注意的是,请求头必须是Authorization,值是Bearer空格加上token。这样访问资源时,通过HTTP header携带令牌信息,服务端,通过认证中间件,完成授权认证过程。在上面的示例中,通过向全局Ajax注册事件,将token写入请求Header。、
至此,就完成了JWT认证授权的过程,.Net Core WebAPI配置起来也很简单。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?