用JWT来保护我们的ASP.NET Core Web API
? 在中,自己动手写了一个Middleware来处理API的授权验证,现在就采用另外一种方式来处理这个授权验证的问题,毕竟现在也 有不少开源的东西可以用,今天用的是JWT。 什么是JWT呢?JWT的全称是JSON WEB TOKENS,是一种自包含令牌格式。官方网址:,或多或少应该都有听过这个。 先来看看下面的两个图: 站点是通过RPC的方式来访问api取得资源的,当站点是直接访问api,没有拿到有访问权限的令牌,那么站点是拿不到相关的数据资源的。
就像左图展示的那样,发起了请求但是拿不到想要的结果;当站点先去授权服务器拿到了可以访问api的access_token(令牌)后,再通过这个 access_token去访问api,api才会返回受保护的数据资源。 这个就是基于令牌验证的大致流程了。可以看出授权服务器占着一个很重要的地位。 下面先来看看授权服务器做了些什么并如何来实现一个简单的授权。 做了什么?授权服务器在整个过程中的作用是:接收客户端发起申请access_token的请求,并校验其身份的合法性,最终返回一个包含 access_token的json字符串。 如何实现?我们还是离不开中间件这个东西。这次我们写了一个TokenProviderMiddleware,主要是看看invoke方法和生成access_token 的方法。
(!
(!context.Request.Method.Equals( || ! }
Invoke方法其实是不用多说的,不过我们这里是做了一个控制,只接收POST请求,并且是只接收以表单形式提交的数据,GET的请求和其 他contenttype类型是属于非法的请求,会返回bad request的状态。 下面说说授权中比较重要的东西,access_token的生成。
GetJwt( now =
claims =
jwt = encodedJwt =
response =
access_token = expires_in = ( token_type =
JsonConvert.SerializeObject(response, JsonSerializerSettings { Formatting = }
?
claims包含了多个claim,你想要那几个,可以根据自己的需要来添加,JwtRegisteredClaimNames是一个结构体,里面包含了所有的可选项。 Acr = Actort = Amr = AtHash = Aud = AuthTime = Azp = Birthdate = CHash = Email = Exp = FamilyName = Gender = GivenName = Iat = Iss = Jti = NameId = Nbf = Nonce = Prn = Sid = Sub = Typ = UniqueName = Website = }
还需要一个JwtSecurityToken对象,这个对象是至关重要的。有了时间、Claims和JwtSecurityToken对象,只要调用JwtSecurityTokenHandler 的WriteToken就可以得到类似这样的一个加密之后的字符串,这个字符串由3部分组成用‘.’分隔。每部分代表什么可以去官网查找。 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
最后我们要用json的形式返回这个access_token、access_token的有效时间和一些其他的信息。 还需要在Startup的Configure方法中去调用我们的中间件。 audienceConfig = Configuration.GetSection( symmetricKeyAsBase64 = audienceConfig[ keyByteArray = signingKey =
app.UseTokenProvider( Audience = Issuer = SigningCredentials = });
到这里,我们的授权服务站点已经是做好了。下面就编写几个单元测试来验证一下这个授权。 测试一:授权服务站点能生成正确的jwt。
data = Dictionary<,> data.Add(, data.Add(, HttpContent ct =
System.Net.Http.HttpResponseMessage message_token = _client.PostAsync( res = obj = Newtonsoft.Json.JsonConvert.DeserializeObject
测试二:授权服务站点因为用户名或密码不正确导致不能生成正确的jwt。
data = Dictionary<, HttpContent ct =
System.Net.Http.HttpResponseMessage message_token = _client.PostAsync( res = obj =
Assert.Equal(,( }
测试三:授权服务站点因为不是发起post请求导致不能生成正确的jwt。
Uri uri = Uri(
System.Net.Http.HttpResponseMessage message_token = res = obj =
Assert.Equal(,( }
再来看看测试的结果:
? 都通过了。
断点拿一个access_token去 解密看看 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJNZW1iZXIiLCJqdGkiOiI2MzI1MmE1My0yMjY5LTQ4YzEtYmQwNi1lOWRiMzdmMTRmYTQiLCJpYXQiOiIyMDE2LzExLzEyIDI6NDg6MTciLCJuYmYiOjE0Nzg5MTg4OTcsImV4cCI6MTQ3ODkxOTQ5NywiaXNzIjoiaHR0cDovL2NhdGNoZXIxOTk0LmNuYmxvZ3MuY29tLyIsImF1ZCI6IkNhdGNoZXIgV29uZyJ9.Cu2vTJ4JAHgbJGzwv2jCmvz17HcyOsRnTjkTIEA0EbQ
下面就是API的开发了。 这里是直接用了新建API项目生成的ValueController作为演示,毕竟跟ASP.NET Web API是大同小异的。这里的重点是配置 JwtBearerAuthentication,这里是不用我们再写一个中间件了,我们是定义好要用的Option然后直接用JwtBearerAuthentication就可以了。 audienceConfig = Configuration.GetSection( symmetricKeyAsBase64 = audienceConfig[ keyByteArray = signingKey =
tokenValidationParameters =
ValidateIssuerSigningKey = IssuerSigningKey =
ValidateIssuer = ValidIssuer =
ValidateAudience = ValidAudience =
ValidateLifetime =
ClockSkew =
app.UseJwtBearerAuthentication( AutomaticAuthenticate = AutomaticChallenge = TokenValidationParameters = }
然后在Startup的Configure中调用上面的方法即可。 loggerFactory.AddConsole(Configuration.GetSection(
}
到这里之后,大部分的工作是已经完成了,还有最重要的一步,在想要保护的api上加上Authorize这个Attribute,这样Get这个方法就会要 求有access_token才会返回结果,不然就会返回401。这是在单个方法上的,也可以在整个控制器上面添加这个Attribute,这样控制器里面的方 法就都会受到保护。
[HttpGet( Get( }
OK,同样编写几个单元测试验证一下。 测试一:valueapi在没有授权的请求会返回401状态。
HttpResponseMessage message = _client.GetAsync( result =
}
?测试二:valueapi请求没有[Authorize]标记的方法时能正常返回结果。
HttpResponseMessage message = _client.GetAsync( result = res = Newtonsoft.Json.JsonConvert.DeserializeObject<[]>
Assert.Equal( }
?测试三:valueapi在授权的请求中会返回正确的结果。
data = Dictionary<, HttpContent ct =
obj = _client.DefaultRequestHeaders.Add(, + HttpResponseMessage message = _client.GetAsync( result =
Assert.Equal(,obj.access_token.Split( Assert.Equal( }
? 再来看看测试的结果:
测试通过。
再通过浏览器直接访问那个受保护的方法。响应头就会提示www-authenticate:Bearer,这个是身份验证的质询,告诉客户端必须要提供相 应的才能访问这个资源(api)。 ? ? 这也是为什么在单元测试中会添加一个Header的原因,正常的使用也是要在请求的报文头中加上这个。 ?, + obj.access_token);? 其实看一下源码,更快知道为什么。 下图是关于头部加Authorization的源码解释。
?
?
本文的示例代码:
?
?
Thanks for your reading!!!
?
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – 为什么ASP.Net MVC(或CodeIgniter)中没有内
- asp.net – 实体框架:Singletonish ObjectContext – Good
- asp.net-mvc – ASP.NET MVC V2 – 好友类
- asp.net-mvc – 如何在ASP.NET MVC中围绕AreaRegistration.
- asp.net-core – ASP.Net 5类库中的EntityFramework命令?
- asp.net-mvc-2 – 从MCV2视图中的模型集合中读取DataAnnota
- ASP.NET MVC对不存在的用户进行身份验证和授权
- 每个请求的ASP.NET 5(VNext)Autofac实例
- ASP.net MVC项目结构
- asp.net mvc 3区域和url路由配置
- asp.net – ASP MVC使用超链接控制
- entity-framework-4 – 在我的自定义MembershipP
- asp.net – OWIN AuthorizeEndpoint,其redirect_
- asp.net – 显示和隐藏转发器中的特定列?
- 在ASP.NET中使用Visual Studio发布功能有什么好处
- ASP.NET Core 2.0 支付宝当面付--扫码支付
- 在asp.net中动态构建SiteMapPath
- ASP.NET 5针对dnx451 / dnx46性能
- asp.net-mvc – 尝试执行多项目ASP.Net MVC站点时
- 在asp.net核心mvc中集成ckeditor