阅读本文你的收获
- 了解认证和授权的作用
- 了解在ASP.NET Core中实现身份认证的技术都有哪些
- 学习基于JWT认证并学会颁发和验证JWT令牌
一、重要的前置概念
在一个系统中,不是所有的功能和资源都能够被自由地访问,比如你存在银行系统里面的资金,不可能给我去提取,对吧。这就需要系统对用户进行一些访问的控制,从而使系统更加地安全。
Authentication(认证)和Authorization(授权)都是用于访问控制的概念,目的是确保只有合法用户可以访问系统资源。
-
Authentication(认证)是指
验证用户身份的过程
。通过认证,系统能够确认用户是真实的,准确地知道用户是谁。常见的认证方式包括用户名和密码、指纹识别、身份证验证等。认证成功后,用户才能被认为是合法用户,可以继续访问系统资源。 -
Authorization(授权)是指
确定用户是否有权限访问特定资源的过程
。认证成功后,系统需要根据用户的角色、权限等信息来判断用户是否有权访问某个资源。授权机制可以基于用户角色或权限级别进行,例如管理员拥有更高的权限,能够访问更多的资源,而普通用户只能访问受限资源。授权也可以是细粒度的,例如给用户赋予特定的操作权限,如读取、写入、删除等。
在ASP.NET Core中,可以使用以下技术实现身份认证(包括但不限于):
-
Cookie身份认证:使用ASP.NET Core的认证中间件,将用户的认证信息存储在cookie中。可以使用
AddAuthentication
和AddCookie
方法配置Cookie身份认证。 -
JWT(JSON Web Token)身份认证:使用JWT作为认证令牌,将用户的认证信息加密并传输给客户端。可以使用
AddJwtBearer
方法配置JWT身份认证。 -
OAuth身份认证:使用OAuth协议进行身份认证,允许用户使用第三方身份提供者进行登录。可以使用
AddAuthentication
和AddOAuth
方法配置OAuth身份认证。 -
OpenID Connect身份认证:基于OAuth协议的扩展,提供了更加丰富的身份认证功能。可以使用
AddAuthentication
和AddOpenIdConnect
方法配置OpenID Connect身份认证。 -
Session认证:ASP.NET Core中可以使用
AddSession
方法来配置会话认证。在使用会话认证时,服务器会为每个用户创建一个会话对象,并分配一个唯一的会话ID。服务器会将该会话ID存储在用户的浏览器cookie中,并在后续的请求中使用该会话ID来验证用户的身份。
以上技术可以根据具体的需求和场景进行选择和配置,也可以组合使用来实现更加复杂的身份认证方案。
二、JWT是什么?
JWT:JSON Web Token (JSON网络令牌)里面存的是JSON格式的数据,有三部分组成,以.号做分割, 头部.荷载.防伪签名
, 经过一定的加密算法来生成。
作用:颁发给登录的用户,用户拿着这个令牌作为身份的凭证,来访问后续的授权资源。
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
2.1 JWT的组成结构
JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
- Header – 头部
- Payload -荷载
- Signature -签名
例子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJMb2dpbk5hbWUiOiJhZG1pbiIsIlN1cGVyQWRtaW4iOiJ0cnVlIiwibmJmIjoxNjM5NTQ4MDYyLCJleHAiOjE2Mzk1NDgyNDIsImlzcyI6IldYRiIsImF1ZCI6IkV2ZXJ5VGVzdE9uZSJ9.rfMYR5h26sv3j-WrjVgqB0K05fr9M4wFgCx4leZnqg8
可以到jwt.io官网上将以上JWT字符串进行调试,解析出原始的JSON数据。
2.2 JWT加密组装的过程
JWT令牌=Base64编码的header
.Base64编码的payload
.Base64编码的signature
;
而signature又是通过HS256这种算法将Base64编码的header
.Base64编码的payload
的字符串进行加密所得。请看下面的演示代码:
//头部
header = {"alg": "HS256", "typ": "JWT"}
//荷载
payload = {"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
//生成签名
key = "secretkey123qwe!@#" //密钥可以自己设置
unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload)
signature = HMAC-SHA256(key, unsignedToken)
//最后的JWT Token如下:
token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)
二、JWT的使用场景
(1)Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
(2)Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。
三、 JWT认证入门案例
开发环境:
操作系统: Windows 10 专业版
平台版本是:.NET 6
开发框架:ASP.NET Core WebApi
开发工具:Visual Studio 2022
3.1、颁发令牌
- 登录成功后,给用户颁发一个JWT的令牌
//引用命名空间
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
namespace XfTech.Demo.WebApi.Controllers;
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize]
public class AccountController : ControllerBase
{
/// <summary>
/// 登录功能
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
[Route("Login")]
[AllowAnonymous] //此方法允许匿名访问
public IActionResult Login(LoginDto input)
{
//第一步做模型验证
if(!ModelState.IsValid) {
//如果输入的参数无效,则直接返回
return BadRequest(); //返回一个400状态码
}
//获取用户(根据输入的用户名和密码做查询,如果用户不为null则登录信息有效,具体请自行实现)
var user = _userService.GetUser(input);
if(user == null)
{
return Ok(new { Code=-1, Msg="用户登录不成功"});
}
//荷载信息
List<Claim> claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
//new Claim("role","guest"),
//new Claim("sex","男")
};
//签名密钥
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("qwerty123456sdgdgsdgfsdgfsfdg"));
//对秘钥加密
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
//过期时间
DateTime expires = DateTime.Now.AddSeconds(2*3600); //设置了2小时
//实例化JWT安全令牌对象
var jwtToken = new JwtSecurityToken(
issuer: "XF", //发布者
audience: "everyone", //受众(发布给谁用)
claims: claims, //发起人 订阅者
expires: expires,//过期时间
signingCredentials: credentials); //秘钥
//序列化jwt令牌 从而得到一个字符串
string strToken = new JwtSecurityTokenHandler().WriteToken(jwtToken);
return Ok(new {
AccessToken = strToken,
Code = 0,
Msg = "用户登成功",
Data = user });
}
}
3.2、验证用户登录(验证JWT令牌)
要想使用JWT进行用户身份验证,必须使用UseAuthentication中间件; 在Program
.cs注册Authentication服务。AddAuthentication
和AddJwtBearer
方法。
//下载NuGet包
Microsoft.AspNetCore.Authentication.JwtBearer
//配置服务
builder.Services.AddAuthentication(option =>
{
//认证模式
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
//质询模式
option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(option =>
{
option.RequireHttpsMetadata = false;//设置元数据地址或权限是否需要HTTPs
option.SaveToken = true;
//Token验证参数
option.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true, //是否验证签名
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("qwerty123456sdgdgsdgfsdgfsfdg")),
ValidIssuer = "XF", //有效的发行人
ValidAudience = "everyone", //有效的受众
ValidateIssuer = true, //是否验证发行人
ValidateAudience = true, //是否验证受众
ClockSkew = TimeSpan.Zero,
ValidateLifetime = true //是否验证过期时间
};
});
//.....
//启用身份验证中间件(不要遗漏哦)
app.UseAuthentication();
app.UseAuthorization();
3.3 、接口测试-检查JWT验证是否有效
- 用[Authorize]特性保护Api
在Api控制器或者方法上,加[Authorize]特性,需要引用命名空间:
using Microsoft.AspNetCore.Authorization;
- [Authorize]加在控制器上,则该控制器下所有API方法需要身份授权后才能访问;
- [Authorize]加在方法上,则仅该方法需要身份授权后才能访问。
另外,有一个[AllowAnonymous]特性,加在Api控制器或者方法上,允许匿名访问该Api或者控制器下所有Api方法。
Api资源被锁保护起来之后,如果没有登录直接访问,则会报401的错误。在Swagger中测试截图如下:
- ApiPost接口测试工具来测试
登录之后拿到了Token令牌,再测试创建用户接口,就能正常返回数据了。
本次是JWT的简单入门,下次我们会对这个JWT进行封装。
如果本文对你有帮助的话,请点赞+评论+关注,或者转发给需要的朋友。