C# 实现Jwtbearer Authentication
Jwtbearer Authentication
什么是JWT
JWT(JSON Web Token), 顾名思义就是在Web上以JSON格式传输的Token(RFC 7519)。
该Token被设计为紧凑声明表示格式,特别适用于分布式站点的单点登录(SSO)场景。
紧凑 :意味着size小,所以可以在URL中,Header中,Post Parameter中进行传输,并且包含了所需要的信息。
JWT的构成
JWT一般由三段构成,用"."号分隔开
Header.Payload.Signature
例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
点击链接 如下图
图左边为Header.Payload.Signature的base64编码
图右构成
Header
- alg:声明加密的算法 ,这里为HS256
- typ:声明类型,这里为JWT
然后将Header进行base64编码 得到第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Payload
由三部分构成
标准中注册的声明
公共的声明
私有的声明
标准中注册的声明 (建议但不强制使用) :
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明 :
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 :
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base6编码可以归类为明文信息 。
定义一个payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}然后将其进行base64加密,得到Jwt的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature(数字签名,防止信息被篡改)
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
Header (base64后的)
Payload (base64后的)
Secret
这个部分需要base64加密后的header和base64加密后的payload使用
.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分 。// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret');
将这三部分用
.
连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
下面我们自己来实现一下 JwtBearer Authentication
- 新建一个WebApi项目
新建JwtSeetings类
public class JwtSeetings
{
/// <summary>
/// 谁颁发的jwt
/// </summary>
public string Issuer { get; set; } /// <summary>
/// 谁使用这个jwt
/// </summary>
public string Audience { get; set; } /// <summary>
/// secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,
/// 所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了
/// 通过jwt header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分
/// </summary>
public string SecretKey { get; set; }
}
appsettings.json里面配置如下
"JwtSeetings": {
"Issuer": "http://localhost:5000",
"Audience": "http://localhost:5000",
"SecretKey": "zhoudafu201807041123"
Startup类里面ConfigureServices添加如下代码
services.Configure<JwtSeetings>(Configuration.GetSection("JwtSeetings")); var jwtSeetings = new JwtSeetings();
//绑定jwtSeetings
Configuration.Bind("JwtSeetings", jwtSeetings);
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; })
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = jwtSeetings.Issuer,
ValidAudience = jwtSeetings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSeetings.SecretKey))
};
})
;
Startup类里面Configure添加如下代码
app.UseAuthentication();
新增AuthroizeController控制器,并添加如下代码
[HttpPost]
public ActionResult Post([FromBody]LoginViewModel loginViewModel)
{ if (!ModelState.IsValid)
{
return BadRequest();
}
if (loginViewModel.Name == "jack" && loginViewModel.Password == "rose")
{ var claims = new Claim[]
{
new Claim(ClaimTypes.Name,"jack"),
new Claim(ClaimTypes.Role,"admin")
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSeetings.SecretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken(
_jwtSeetings.Issuer,
_jwtSeetings.Audience,
claims,
DateTime.Now,
DateTime.Now.AddMinutes(30),
creds
);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
} return BadRequest();
}
给ValuesController控制器打上[Authorize]特性
用Postman直接访问http://localhost:5000/api/Values 返回401
用Postman访问http://localhost:5000/api/Authroize 得到Token
通过Bearer访问成功
参考博客:https://www.jianshu.com/p/576dbf44b2ae
最新文章
- Daily Scrum02 12.11
- Lamp(Ubuntu 12.04 LTS) 之 htaccess的使用
- [ZigBee] 4、ZigBee基础实验——中断
- <;转>;修改TM2013聊天记录保存目录final版
- 将WeX5部署到自己的Tomcat服务器上
- BZOJ 3036 绿豆蛙的归宿
- comet基于HTTP长连接技术(java即时通信,推送技术详解)
- requirejs实验002. r.js合并文件. 初体验.
- c#基础: 线程的初级用法总结
- javase程序设计课后题答案
- tomcat识别不出maven web项目
- MFC滚动条实现要点
- css 定位属性position的使用方法实例-----一个层叠窗口
- Oracle 11.2.0.1的又一个隐藏在ORA-03113后的bug: 通信通道的文件结尾
- 学习day01
- better-scroll
- Spark学习笔记——在远程机器中运行WordCount
- oracle取出多个字段列中的最大值和最小值
- 1.3.2、CDH 搭建Hadoop在安装之前(端口---Cloudera Navigator加密使用的端口)
- (转)在WCF服务的ServiceReferences.ClientConfig中使用相对路径