1、JWT介绍
网上一搜就有很多关于JWT的介绍
官网上的介绍 : JSON Web Token Introduction - jwt.io
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。此信息可以被验证和信任,因为它是经过数字签名的。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
尽管JWT可以被加密以提供各方之间的保密性,但我们将重点关注签名令牌。签名令牌可以验证其中包含的声明的完整性,而加密令牌对其他方隐藏这些声明。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方是对其进行签名的一方。
以前使用的较多的是Cookie认证(Asp.net Core3.1 Cookie认证 简单使用_ShanShanYouWen的博客-CSDN博客) 相对于一些小项目,不需要分布式部署的,认证和web应用同在一个项目里部署。现如今移动端APP对应的服务端一般都是无状态的API,JWT是无状态的支持分布式的身份验证方式。
2、JWT主要组成部分
Header.Payload.Signature
Header:
标头通常由两部分组成:令牌类型(JWT)和使用的签名算法(如HMAC SHA256或RSA)
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
令牌的第二部分是有效载荷,其中包含声明。声明是关于实体(通常是用户)和其他数据的声明。索赔有三种类型:registered, public, and private claims.。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Signature:
要创建签名部分,您必须获取编码的头、编码的有效载荷、秘密、头中指定的算法,并签名.
HMAC SHA256算法如下:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
3、JWT认证流程
下图显示了如何获取JWT并将其用于访问API或资源:
认证流程:
- 客户端通过认证服务器获取一个Token。
- 在访问应用服务器或资源服务器的API的时候,将获取到的Token放置在请求的Header中。
- 应用服务器或资源服务器验证该Token,通过后返回对应的结果。
4、NetCore3.1 使用JWT认证
创建新的Asp.Net Web 项目
加载JwtBearer包
环境是NetCore3.1 所以选择3.1.30 版本
4.1、在配置文件配置JWT需要用到的参数
"JwtConfig": {
"Issuer": "issuer",
"Audience": "audience",
"SigningKey": "ac0d32c63@z43d-8717-vcsd-32fsdferuwqqvj",
//过期时间(分钟)
"Expires": 120,
//是否验证过期时间
"ValidateLifetime": true
},
4.2、在Startup类中读取配置参数
services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));
var jwtConfig = Configuration.GetSection("JwtConfig").Get<JwtConfig>();
4.3、添加JWT认证服务
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuerSigningKey = true,//是否调用对签名securityToken的SecurityKey进行验证
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SigningKey)),
ValidIssuer = jwtConfig.Issuer,//将用于检查令牌的发行者是否与此发行者相同
ValidAudience = jwtConfig.Audience,//检查令牌的受众群体是否与此受众群体相同
ValidateLifetime=jwtConfig.ValidateLifetime,
ValidateIssuer = false,//是否验证发行者
ValidateAudience = false//在令牌验证期间验证受众
};
});
4.4、添加中间件使用:
app.UseAuthentication();
注: 一定要放在 app.UseRouting 和 app.UseEndpoints 中间。
4.5、如果要使认证生效,需要在方法或控制器上加特性 [Authorize], 需要授权才会生效。
先测试一下,在默认的方法Privacy 上对比一下未添加特性 [Authorize] 和添加特性之后的效果
未添加之前点击Privacy,显示
添加特性后
[Authorize]
public IActionResult Privacy()
{
return View();
}
启动项目,点击Privacy,弹出错误提示 401 未授权,无权限访问方法。
4.6、调用方法生成Token,实现JWT认证
private string GenerateToken(JwtConfig jwtConfig, User user)
{
var claims = new Claim[] {
new Claim (ClaimTypes.Name,user.username)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SigningKey));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var securityToken = new JwtSecurityToken(
jwtConfig.Issuer,
jwtConfig.Audience,
claims,
expires: DateTime.Now.AddMinutes(jwtConfig.Expires),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(securityToken);
}
需要调用到配置参数JwtConfig以及登录时用户名称
定义参数类JwtConfig
public class JwtConfig
{
/// <summary>
/// 谁颁发的
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 颁发给谁
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 令牌密码
/// a secret that needs to be at least 16 characters long
/// </summary>
public string SigningKey { get; set; }
/// <summary>
/// 过期时间(分钟)
/// </summary>
public int Expires { get; set; }
/// <summary>
/// 是否校验过期时间
/// </summary>
public bool ValidateLifetime { get; set; }
}
用户类User:
public class User
{
public int ID { get; set; }
public string username { get; set; }
public string pwd { get; set; }
}
简单获取用户信息的方法:
public class UserBLL
{
public User GetUser(string username, string pwd)
{
return new User() { ID=1, username = username, pwd = pwd };
}
}
在控制器构造函数增加获取配置参数JwtConfig:
private readonly ILogger<HomeController> _logger;
private readonly JwtConfig _jwtoptions;
public HomeController(ILogger<HomeController> logger, IOptions<JwtConfig> jwtoptions)
{
_logger = logger;
_jwtoptions = jwtoptions.Value;
}
登录方法:
[HttpPost]
public IActionResult Login(string username, string pwd)
{
var user = new UserBLL().GetUser(username, pwd);
if (user != null)
{
string token = GenerateToken(_jwtoptions, user);
return Ok(new { code = 0, msg = "success", Token = token }) ;
}
return NoContent();
}
前端页面:
<input id="username" name="username" type="text" />
<input id="pwd" name="pwd" type="password" />
<input type="submit" value="登录" onclick="submit()" />
前端ajax调用登录:
var token="";
function submit()
{
$.ajax({
url: "/Home/Login",
type: "post",
dataType: "json",
data: {'username':$("#username").val(),"pwd":$("#pwd").val()},
async: false,
success: function (d) {
var obj = JSON.stringify(d);
console.log(obj);
token = d.token;
},
error: function (d) {
alert(JSON.stringify(d))
}
});
}
要把登录后返回的token 保存
然后在页面添加一个a 标签用于访问授权的方法
<a id="myinfo" style="color:blue" onclick="myinfo()">My Info</a>
function myinfo()
{
$.ajax({
url: "/Home/Info",
type: "get",
dataType: "json",
async: false,
headers: { "Authorization": "Bearer " + token },
success: function (d) {
alert(d);
console.log(JSON.stringify(d));
$("#myinfo").html(d.data);
},
error: function (d) {
//alert(JSON.stringify(d))
}
});
}
[Authorize]
public IActionResult Info()
{
return Ok(new { code = 0, msg = "success", data = "你有权限访问我的信息" });
}
这样我们在前端不仅获取到token,还在调用方法的时候用上了token
headers: { "Authorization": "Bearer " + token }
运行项目
输入账号密码登录成功后,点击My Info
提示已有权限,点击Privacy,依旧无权限提示,因为我们没有在Header上加上Token
我们用到第三方工具Postman调用接口,把token加上试试:
未添加token,提示401 无权限
添加token,访问
简单的调用实现JWT已完成,后续要封装的可以在此基础上完善。
网上参考看了许多文章,有很多封装很好,此文章只是简单的使用,初体验。
参考文章:JSON Web Token Introduction - jwt.io
『JWT』,你必须了解的认证登录方案 - 风的姿态 - 博客园