结合.NET Core学习JWT
一 JWT是什么
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
二 JWT的作用是什么
- 授权(Authorization )
- 例如登录。可以通过登录API获取JWT,后续每一个请求中都包含JWT。
三 JWT的结构
Header.Payload.Signature
三部分组成,中间使用.
连接,Head和Payload通过Base64加密
- Header(头部)
- Header典型的由两部分组成:声明和类型。
- 算法
alg
,algorithm,表示签名使用的算法,默认为HMAC SHA256 写为HS256 - 类型
typ
,type,表示令牌的属性,JWT令牌统一写为JWT - 例如:
- 算法
- Header典型的由两部分组成:声明和类型。
{
'alg':'HS256',
'typ':'JWT'
}
- 以上信息通过Base64加密获取第一段信息
- Payload(有效荷载)
- 是JWT的主体部分。包含声明(要求)。就是传递一些数据(一般是用户的信息)。
- JWT指定了一些默认字段供选择。例如:
- iss:发行人
- exp:到期时间
- sub:主题 subject 就是id
- aud:用户
- nbf:在此之前不可用
- iat:发布时间
- jti:JWT ID用于标识该JWT
- 还可以使用自定义字段
{
"name":"wwwk",
"role":"admin"
}
-
需要注意的是,默认情况下,JWT是未加密的,任何人都可以解读内容 ,因此不要构建一些机密数据的字段,防止信息泄露。除非自己尽心加密操作。
-
Signature(签名)
- 签名是对上面两个部分的数据,通过指定的算法生成哈希,确保数据没有被篡改过。
- 首先需要指定一个
secret
,然后使用头部中指定的算法,根据一下公式生成签名。HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
- 得到哈希之后,和上面两个部分通过
.
分割,组成JWT对象。
四 查看方式
- 可以通过以下方式查看
- jwt.io
- chrome 控制台 atob (查看jwt中间Payload部分)
五 如何通过.Net Core创建JWT
1. 简单的创建
新建Web
程序👉选择API
模板👉关闭为HTTPS配置将Controllers
文件夹中的WeatherForecastController.cs
,重命名为JwtController.cs
将其代码修改为:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace Jwt.Controllers
{
[ApiController]
[Route("[controller]")]
public class JwtController : ControllerBase
{
public JwtController()
{
}
[HttpGet]
public IEnumerable<string> Get()
{
return new List<string>
{
string.Empty
};
}
}
}
首先,在Get
方法中创建一组声明。
需要引入的命名空间及包。
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
namespace Jwt.Controllers
{
[ApiController]
[Route("[controller]")]
public class JwtController : ControllerBase
{
public JwtController()
{
}
[HttpGet]
public IEnumerable<string> Get()
{
var claims = new Claim[]
{
// .NET Core 自带的一些声明
new Claim(ClaimTypes.Name, "wwwk"),
// JWT的声明
new Claim(JwtRegisteredClaimNames.Email, "wwwk@qq.com"),
// 自定义声明
new Claim(UserClaimTypes.Id, "123456789")
};
// 实例token对象
var simpleToken = new JwtSecurityToken(claims: claims);
// 生成token
var jwtToken = new JwtSecurityTokenHandler().WriteToken(simpleToken);
return new List<string>
{
jwtToken
};
}
}
class UserClaimTypes
{
public const string Id = "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid";
}
}
启动项目之后使用Postman
进行调用,我们就可以获得一个简单的token
然后通过jwt.io
网站,或者自行解析获得如下数据。
可以看到现在我们获取到的串只有两部分,Header
和Payload
。Header
中的alg
为none
。
其中我们定义的数据也获取了出来,是一个标准的json
。三种声明方式获得了三种不同的key
。
2. 正常JWT的创建
包含签名和基本的定义。
using System;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace Jwt.Controllers
{
[ApiController]
[Route("[controller]")]
public class JwtController : ControllerBase
{
public JwtController()
{
}
[HttpGet]
public IEnumerable<string> Get()
{
var claims = new Claim[]
{
// .NET Core 自带的一些声明
new Claim(ClaimTypes.Name, "wwwk"),
// JWT的声明
new Claim(JwtRegisteredClaimNames.Email, "wwwk@qq.com"),
// 自定义声明
new Claim(UserClaimTypes.Id, "123456789")
};
// 实例token对象
//var simpleToken = new JwtSecurityToken(claims: claims);
// 密钥
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdefghigklmn"));
var token = new JwtSecurityToken(
// 发行人
issuer: "wwwk",
// 订阅
audience: "wwwk",
// 声明
claims: claims,
// 过期日期
expires: DateTime.Now.AddHours(-1),
// 数字加密,它由两部分组成,密钥和加密方式
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
);
// 生成token
var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
return new List<string>
{
jwtToken
};
}
}
class UserClaimTypes
{
public const string Id = "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid";
}
}
再次通过postman
进行调用。
发现抛出了一个异常!
这个异常的意思是说咱们定义key
的长度太短,对它进行了约束,最低要16字符。这里我们将key
加长至16
位:var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdefghigklmnop"));
再次调用接口,成功返回:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoid3d3ayIsImVtYWlsIjoid3d3a0BxcS5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3ByaW1hcnlzaWQiOiIxMjM0NTY3ODkiLCJleHAiOjE2MTc3MjA4MDEsImlzcyI6Ind3d2siLCJhdWQiOiJ3d3drIn0.XcvcSvqJZGk17kR6n3CQ7Yi1vxnbxiJlXTWjfqZrQH4
JWT.IO
网站进行查看内容。alg
为HS256
,Payload
中的内容也显示出来了,但是左下角显示的却是无效的签名**Invalid Signature**
这是因为左下角蓝色部分的密钥和咱们的不同导致的,此时将密钥修改为abcdefghigklmnop
再次尝试。
可以看到,修改密钥之后,成功显示Signature Verified
。密钥是很重要的,如果第三方获取到密钥就可以伪装数据进行操作,千万不能泄露!
3. 获取接口中携带的JWT内容
上面我们获取到了JWT,那怎么在调用接口的时候发送JWT并在接口内获取其数据呢?
现在,我们创建一个新的接口:
[HttpGet("Invoke")]
public IEnumerable<string> Get(string jwt)
{
// 第一种,jwtHandle提供的read方法
var jwtHandler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwt);
// 第二种,通过User对象接收
var sub = User.FindFirst(p => p.Type == UserClaimTypes.Id)?.Value;
// 第三种,通过HttpContext上下文直接进行获取
var name = _httpContextAccessor.HttpContext.User.Identity.Name;
var claims = _httpContextAccessor.HttpContext.User.Claims;
var val = claims.Select(p => p.Type == JwtRegisteredClaimNames.Email).ToList();
return new List<string>
{
System.Text.Json.JsonSerializer.Serialize(jwtToken),
sub,
name,
System.Text.Json.JsonSerializer.Serialize(val)
};
}
然后在Startup.cs
👉ConfigureServices
方法,添加services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
通过Get
接口获取Token
,然后通过Invoke
接口进行调用结果。
可以发现,第一种获取成功,其它两种都是为null
;这是因为,通过httpcontext
获取,需要在服务里注册一个认证,我们才能获取到内容,进一步才能获得鉴权。
4. 添加服务
第一步:
添加NeGet
包Microsoft.AspNetCore.Authentication.JwtBearer
然后在Startup.cs
👉ConfigureServices
方法,添加以下代码:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// 添加IHttpContextAccessor实例到容器中
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// token验证参数
var tokenValidationParameters = new TokenValidationParameters
{
// 是否验证IssuerSigningKey
ValidateIssuerSigningKey = true,
// key必须都一样
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdefghigklmnop")),
// 是否验证Issuer
ValidateIssuer = true,
ValidIssuer = "wwwk",
// 是否验证 Audience
ValidateAudience = true,
ValidAudience = "wwwk",
// 总的Token有效时间 = JwtRegisteredClaimNames.Exp + ClockSkew ;
RequireExpirationTime = true,
// 是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比.同时启用ClockSkew
ValidateLifetime = true,
// 注意这是缓冲过期时间,总的有效时间等于这个时间加上jwt的过期时间,如果不配置,默认是5分钟
ClockSkew = TimeSpan.Zero
};
// 注入服务
services
// 开启Bearer验证
.AddAuthentication("Bearer")
// 注册JwtBearer服务
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters
});
}
具体的意思看注释就行,大体上就是创建**TokenValidationParameters**
对象,这个对象就是验证参数的设置,看注释就能明白大概了。如果开启验证那就要和生成JWT的参数值设为一样,比如Key
密钥,如果不一样那就验证失败了,可以把验证认作是一个解密的过程。
然后注入服务AddAuthentication
。
第二步:
使用.Net Core自带的授权中间件,这个比较重要,如果不使用的话,就无法从管道中将jwt数据加到httpcontext上下文中。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
// 验证中间件
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
第三步:
给Invoke接口添加[Authorize]特性,这样就表示这个接口需要授权才能访问。
通过Postman调用,在请求HEADER中添加Authorization
,输入通过Get
获取的Token
。
如果随便输入一个Token或不输入,则抛出401
异常(下图右上角)。
简单入门完成。
本文作者:hiwwwk
本文链接:https://www.cnblogs.com/wwwk/p/15894058.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步