WebAPI 应用JSON Web Token(jwt)
WebAPI 不能像MVC项目中应用cookie机制在浏览器中保存用户验证信息,需要在每次Request请求中附件Token信息,常见的有方式有在请求的url后附加appKey参数信息,或者在header中附加token信息,本文将介绍一种目前较为成熟的Bearer 验证方式
添加 MessageHandler
首先,新建WebAPI项目,安装 package JwtAuthForWebAPI
Install-Package JwtAuthForWebAPI
然后,在WebApiConfig类中的Register方法中添加:
var builder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
AllowedAudience = "http://www.example.com",
Issuer = "corp",
SigningToken = builder.CreateFromCertificate("CN=JwtAuthForWebAPI Example"),
CookieNameToCheckForToken = "ut"
};
config.MessageHandlers.Add(jwtHandler);
此处的参数也可通过web.config配置:
<configuration>
<configSections>
<section name="JwtAuthForWebAPI" type="JwtAuthForWebAPI.JwtAuthForWebApiConfigurationSection" />
</configSections>
<JwtAuthForWebAPI AllowedAudience="http://www.example.com" Issuer="corp" SymmetricKey="cXdlcnR5dWlvcGFzZGZnaGprbHp4Y3Zibm0xMjM0NTY=" />
</configuration>
这样,WebApiConfig类中的Register方法修改如下:
var builder = new SecurityTokenBuilder();
var reader = new ConfigurationReader();
config.MessageHandlers.Add(
new JwtAuthenticationMessageHandler
{
AllowedAudience = reader.AllowedAudience,
Issuer = reader.Issuer,
SigningToken = builder.CreateFromKey(reader.SymmetricKey)
});
配置完毕后,在Controller中添加[Authorize]筛选器后,再次请求便得到401 Unauthorized响应,此时我们需要在header中添加token方能得到正常的响应,下一步将演示如何生成token
生成Token
一般将生成token的方法放在验证用户登录方法中,即判断用户登录成功后,将用户的ID,Name和Role等信息存放在Claim中,再生成token,返回给客户端,方法如下:
string CreateJwt(UserDto user)
{
var reader = new ConfigurationReader();
var key = Convert.FromBase64String(reader.SymmetricKey);
var credentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(key),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
var claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier,user.ID,
new Claim(ClaimTypes.Name, user.Name)
});
var roleClaims = user.Roles.Select(r => new Claim(ClaimTypes.Role, r.RoleName));
claimsIdentity.AddClaims(roleClaims);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = claimsIdentity,
TokenIssuerName = reader.Issuer,
AppliesToAddress = reader.AllowedAudience,
SigningCredentials = credentials,
Lifetime = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddDays(10))
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return tokenString;
}
Header中添加token调用WebAPI
客户端在首次登录成功,得到token后,在后续的请求中,需在header中附加token信息,具体附加方式为:
在header中添加key:Authorization,value:Bearer token,注意此处Bearer和token中间有一个空格
假如上一步得到的token为:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1laWQiOiJjM2FiYjU2Yy1mYTEzLTQ3M2MtODY2NC00MjQzZWIxY2UwYWIiLCJ1bmlxdWVfbmFtZSI6ImFkbWluIiwiZ3JvdXBzaWQiOiJDR1EiLCJyb2xlIjpbIlVzZXIiLCJBZG1pbiJdLCJpc3MiOiJjb3JwIiwiYXVkIjoiaHR0cDovL3d3dy5leGFtcGxlLmNvbSIsImV4cCI6MTUyMzI2MDYwMCwibmJmIjoxNTIyMzk2NjAwfQ.DoGnM87RfaqB9amFOjxJaODDWimlITHhh7LY5GZEI8Q
则请求如下:
curl -X GET "http://localhost:49221/api/Value" -H "accept: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1laWQiOiJjM2FiYjU2Yy1mYTEzLTQ3M2MtODY2NC00MjQzZWIxY2UwYWIiLCJ1bmlxdWVfbmFtZSI6ImFkbWluIiwiZ3JvdXBzaWQiOiJDR1EiLCJyb2xlIjpbIlVzZXIiLCJBZG1pbiJdLCJpc3MiOiJjb3JwIiwiYXVkIjoiaHR0cDovL3d3dy5leGFtcGxlLmNvbSIsImV4cCI6MTUyMzI2MDYwMCwibmJmIjoxNTIyMzk2NjAwfQ.DoGnM87RfaqB9amFOjxJaODDWimlITHhh7LY5GZEI8Q"
若客户端是C#代码,参考如下:
HttpRequestHeaders headers;
headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
Swagger 配置
WebAPI 添加权限验证后,Swagger调用也要附加token信息,方式如下:
安装package Swagger-Net
Install-Package Swagger-Net
修改SwaggerConfig类中Register中的EnableSwagger方法:
//c.ApiKey("apiKey", "header", "API Key Authentication");
修改为:
c.ApiKey("Authorization", "header", "Filling bearer token here");
在Swagger的Authorization 页面中,添加Value值:Bearer token 即可
在其他版本的Swagger package中,上述的c.ApiKey只支持一个形参,参照如下修改:
修改SwaggerConfig类中Register中的EnableSwagger方法:
c.ApiKey("Authorization")
.Description("Filling bearer token here")
.In("header");
同时,修改SwaggerConfig类中Register中的EnableSwaggerUi方法:
.EnableSwaggerUi(c =>
{
c.EnableApiKeySupport("Authorization", "header");
});