出口网站怎么做,wordpress高级靶机,有什么做图片赚钱的网站,旅游网站开发研究背景在用Middleware给ASP.NET Core Web API添加自己的授权验证 中#xff0c;自己动手写了一个Middleware来处理API的授权验证#xff0c;现在就采用另外一种方式来处理这个授权验证的问题#xff0c;毕竟现在也有不少开源的东西可以用#xff0c;今天用的是JWT。 什么是JWT呢自己动手写了一个Middleware来处理API的授权验证现在就采用另外一种方式来处理这个授权验证的问题毕竟现在也有不少开源的东西可以用今天用的是JWT。 什么是JWT呢JWT的全称是JSON WEB TOKENS是一种自包含令牌格式。官方网址https://jwt.io/或多或少应该都有听过这个。 先来看看下面的两个图 站点是通过RPC的方式来访问api取得资源的当站点是直接访问api没有拿到有访问权限的令牌那么站点是拿不到相关的数据资源的。 就像左图展示的那样发起了请求但是拿不到想要的结果当站点先去授权服务器拿到了可以访问api的access_token(令牌)后再通过这个 access_token去访问apiapi才会返回受保护的数据资源。 这个就是基于令牌验证的大致流程了。可以看出授权服务器占着一个很重要的地位。 下面先来看看授权服务器做了些什么并如何来实现一个简单的授权。 做了什么授权服务器在整个过程中的作用是接收客户端发起申请access_token的请求并校验其身份的合法性最终返回一个包含 access_token的json字符串。 如何实现我们还是离不开中间件这个东西。这次我们写了一个TokenProviderMiddleware主要是看看invoke方法和生成access_token 的方法。 /// summary /// invoke the middleware /// /summary /// param namecontext/param /// returns/returns public async Task Invoke(HttpContext context) { if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal)) { await _next(context); } // Request must be POST with Content-Type: application/x-www-form-urlencoded if (!context.Request.Method.Equals(POST) || !context.Request.HasFormContentType) { await ReturnBadRequest(context); } await GenerateAuthorizedResult(context); } Invoke方法其实是不用多说的不过我们这里是做了一个控制只接收POST请求并且是只接收以表单形式提交的数据GET的请求和其 他contenttype类型是属于非法的请求会返回bad request的状态。 下面说说授权中比较重要的东西access_token的生成。 /// summary /// get the jwt /// /summary /// param nameusername/param /// returns/returns private string GetJwt(string username) { var now DateTime.UtcNow; var claims new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, username), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64) }; var jwt new JwtSecurityToken( issuer: _options.Issuer, audience: _options.Audience, claims: claims, notBefore: now, expires: now.Add(_options.Expiration), signingCredentials: _options.SigningCredentials); var encodedJwt new JwtSecurityTokenHandler().WriteToken(jwt); var response new { access_token encodedJwt, expires_in (int)_options.Expiration.TotalSeconds, token_type Bearer }; return JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting Formatting.Indented }); } claims包含了多个claim你想要那几个可以根据自己的需要来添加JwtRegisteredClaimNames是一个结构体里面包含了所有的可选项。 public struct JwtRegisteredClaimNames { public const string Acr acr; public const string Actort actort; public const string Amr amr; public const string AtHash at_hash; public const string Aud aud; public const string AuthTime auth_time; public const string Azp azp; public const string Birthdate birthdate; public const string CHash c_hash; public const string Email email; public const string Exp exp; public const string FamilyName family_name; public const string Gender gender; public const string GivenName given_name; public const string Iat iat; public const string Iss iss; public const string Jti jti; public const string NameId nameid; public const string Nbf nbf; public const string Nonce nonce; public const string Prn prn; public const string Sid sid; public const string Sub sub; public const string Typ typ; public const string UniqueName unique_name; public const string Website website; } JwtRegisteredClaimNames 还需要一个JwtSecurityToken对象这个对象是至关重要的。有了时间、Claims和JwtSecurityToken对象只要调用JwtSecurityTokenHandler 的WriteToken就可以得到类似这样的一个加密之后的字符串这个字符串由3部分组成用‘.’分隔。每部分代表什么可以去官网查找。 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 最后我们要用json的形式返回这个access_token、access_token的有效时间和一些其他的信息。 还需要在Startup的Configure方法中去调用我们的中间件。 var audienceConfig Configuration.GetSection(Audience); var symmetricKeyAsBase64 audienceConfig[Secret]; var keyByteArray Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey new SymmetricSecurityKey(keyByteArray); app.UseTokenProvider(new TokenProviderOptions { Audience Catcher Wong, Issuer http://catcher1994.cnblogs.com/, SigningCredentials new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256), }); 到这里我们的授权服务站点已经是做好了。下面就编写几个单元测试来验证一下这个授权。 测试一授权服务站点能生成正确的jwt。 [Fact] public async Task authorized_server_should_generate_token_success() { //arrange var data new Dictionarystring, string(); data.Add(username, Member); data.Add(password, 123); HttpContent ct new FormUrlEncodedContent(data); //act System.Net.Http.HttpResponseMessage message_token await _client.PostAsync(http://127.0.0.1:8000/auth/token, ct); string res await message_token.Content.ReadAsStringAsync(); var obj Newtonsoft.Json.JsonConvert.DeserializeObjectToken(res); //assert Assert.NotNull(obj); Assert.Equal(600, obj.expires_in); Assert.Equal(3, obj.access_token.Split(.).Length); Assert.Equal(Bearer, obj.token_type); } 测试二授权服务站点因为用户名或密码不正确导致不能生成正确的jwt。 [Fact] public async Task authorized_server_should_generate_token_fault_by_invalid_app() { //arrange var data new Dictionarystring, string(); data.Add(username, Member); data.Add(password, 123456); HttpContent ct new FormUrlEncodedContent(data); //act System.Net.Http.HttpResponseMessage message_token await _client.PostAsync(http://127.0.0.1:8000/auth/token, ct); var res await message_token.Content.ReadAsStringAsync(); dynamic obj Newtonsoft.Json.JsonConvert.DeserializeObject(res); //assert Assert.Equal(invalid_grant, (string)obj.error); Assert.Equal(HttpStatusCode.BadRequest, message_token.StatusCode); } 测试三授权服务站点因为不是发起post请求导致不能生成正确的jwt。 Fact] public async Task authorized_server_should_generate_token_fault_by_invalid_httpmethod() { //arrange Uri uri new Uri(http://127.0.0.1:8000/auth/token?usernameMemberpassword123456); //act System.Net.Http.HttpResponseMessage message_token await _client.GetAsync(uri); var res await message_token.Content.ReadAsStringAsync(); dynamic obj Newtonsoft.Json.JsonConvert.DeserializeObject(res); //assert Assert.Equal(invalid_grant, (string)obj.error); Assert.Equal(HttpStatusCode.BadRequest, message_token.StatusCode); } 再来看看测试的结果 都通过了。 断点拿一个access_token去http://jwt.calebb.net/ 解密看看 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJNZW1iZXIiLCJqdGkiOiI2MzI1MmE1My0yMjY5LTQ4YzEtYmQwNi1lOWRiMzdmMTRmYTQiLCJpYXQiOiIyMDE2LzExLzEyIDI6NDg6MTciLCJuYmYiOjE0Nzg5MTg4OTcsImV4cCI6MTQ3ODkxOTQ5NywiaXNzIjoiaHR0cDovL2NhdGNoZXIxOTk0LmNuYmxvZ3MuY29tLyIsImF1ZCI6IkNhdGNoZXIgV29uZyJ9.Cu2vTJ4JAHgbJGzwv2jCmvz17HcyOsRnTjkTIEA0EbQ 下面就是API的开发了。 这里是直接用了新建API项目生成的ValueController作为演示毕竟跟ASP.NET Web API是大同小异的。这里的重点是配置 JwtBearerAuthentication这里是不用我们再写一个中间件了我们是定义好要用的Option然后直接用JwtBearerAuthentication就可以了。 public void ConfigureJwtAuth(IApplicationBuilder app) { var audienceConfig Configuration.GetSection(Audience); var symmetricKeyAsBase64 audienceConfig[Secret]; var keyByteArray Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyByteArray); var tokenValidationParameters new TokenValidationParameters { // The signing key must match! ValidateIssuerSigningKey true, IssuerSigningKey signingKey, // Validate the JWT Issuer (iss) claim ValidateIssuer true, ValidIssuer http://catcher1994.cnblogs.com/, // Validate the JWT Audience (aud) claim ValidateAudience true, ValidAudience Catcher Wong, // Validate the token expiry ValidateLifetime true, ClockSkew TimeSpan.Zero }; app.UseJwtBearerAuthentication(new JwtBearerOptions { AutomaticAuthenticate true, AutomaticChallenge true, TokenValidationParameters tokenValidationParameters, }); } 然后在Startup的Configure中调用上面的方法即可。 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection(Logging)); loggerFactory.AddDebug(); ConfigureJwtAuth(app); app.UseMvc(); } 到这里之后大部分的工作是已经完成了还有最重要的一步在想要保护的api上加上Authorize这个Attribute这样Get这个方法就会要 求有access_token才会返回结果不然就会返回401。这是在单个方法上的也可以在整个控制器上面添加这个Attribute这样控制器里面的方 法就都会受到保护。 // GET api/values/5 [HttpGet({id})] [Authorize] public string Get(int id) { return value; } OK同样编写几个单元测试验证一下。 测试一valueapi在没有授权的请求会返回401状态。 [Fact] public void value_api_should_return_unauthorized_without_auth() { //act HttpResponseMessage message _client.GetAsync(http://localhost:63324/api/values/1).Result; string result message.Content.ReadAsStringAsync().Result; //assert Assert.False(message.IsSuccessStatusCode); Assert.Equal(HttpStatusCode.Unauthorized,message.StatusCode); Assert.Empty(result); } 测试二valueapi请求没有[Authorize]标记的方法时能正常返回结果。 [Fact] public void value_api_should_return_result_without_authorize_attribute() { //act HttpResponseMessage message _client.GetAsync(http://localhost:63324/api/values).Result; string result message.Content.ReadAsStringAsync().Result; var res Newtonsoft.Json.JsonConvert.DeserializeObjectstring[](result); //assert Assert.True(message.IsSuccessStatusCode); Assert.Equal(2, res.Length); } 测试三valueapi在授权的请求中会返回正确的结果。 [Fact] public void value_api_should_success_by_valid_auth() { //arrange var data new Dictionarystring, string(); data.Add(username, Member); data.Add(password, 123); HttpContent ct new FormUrlEncodedContent(data); //act var obj GetAccessToken(ct); _client.DefaultRequestHeaders.Add(Authorization, Bearer obj.access_token); HttpResponseMessage message _client.GetAsync(http://localhost:63324/api/values/1).Result; string result message.Content.ReadAsStringAsync().Result; //assert Assert.True(message.IsSuccessStatusCode); Assert.Equal(3, obj.access_token.Split(.).Length); Assert.Equal(value,result); } 再来看看测试的结果 测试通过。 再通过浏览器直接访问那个受保护的方法。响应头就会提示www-authenticate:Bearer这个是身份验证的质询告诉客户端必须要提供相 应的身份验证才能访问这个资源(api)。 这也是为什么在单元测试中会添加一个Header的原因正常的使用也是要在请求的报文头中加上这个。 _client.DefaultRequestHeaders.Add(Authorization, Bearer obj.access_token); 其实看一下源码更快知道为什么。JwtBearerHandler.cs 下图是关于头部加Authorization的源码解释。 原文地址http://www.cnblogs.com/catcher1994/p/6057484.html.NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注