目录
- 1、JWT
- 2、具体实现
- 3、代码下载
1、JWT
现在在各类API的开发中,token已经是必备了。例如:微信公众号开发中,第一个方法就是获取token。JWT具体的定义及组成部分大家可以到网上找找,这儿给一个简单的描述:JWT 令牌是紧凑的 URL 安全令牌,易于在各方之间转移。它们是自包含的,这意味着它们自身内部携带信息,从而减少了对服务器端会话存储的需求。
定义可以从网上找到,但网上入门的例子,要么很简单、要么很复杂,反而不知道如何入门。因此这儿记录一下,在net8的webapi中如何使用JWT的入门教程。
2、具体实现
首先创建NET8
环境下WebApi
的项目,通过nuget
引用包 Microsoft.AspNetCore.Authentication.JwtBearer
在appsettings.json
中添加配置信息
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
/**JWT的配置信息,这几个信息都是测试数据,需要根据实际业务自行调整**/
"JwtSettings": {
"SecretKey": "bAafd@A7d9#@F4*V!LHZs#ebKQrkE6pad2f3kj34c3dXy@", /**存放加密的秘钥**/
"Issuer": "ZhengLinTest", /**存放发布者信息**/
"Audience": "AllUseAPI", /**存放受众者信息**/
"AccessTokenExpirationMinutes": 30,
"RefreshTokenExpirationDays": 7
}
}
为了能够将配置文件中JwtSettings
强转换为一个类,需要提前定义一个Model类。
namespace JWTWebApplication.Models
{
/// <summary>
/// 这个类主要是用于解析appsettings.json里的配置信息,也可以使用其他方式,获取及保存配置信息
/// </summary>
public class JwtSettings
{
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int AccessTokenExpirationMinutes { get; set; }
public int RefreshTokenExpirationDays { get; set; }
}
}
JWT是一加密的符合JSON格式的字符串,那我们就需要定义,到底要加密哪些信息。在本例中我们会加密:姓名、密码、角色、邮箱等信息。因此我们创建一个类
,用于自定义需要加密到JWT中的信息。具体的Model如下
namespace JWTWebApplication.Models
{
/// <summary>
/// 用于存储到token中的数据
/// </summary>
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string[] Roles { get; set; }
}
}
下面我们会创建一个用于生成JWT
的Service
类,并添加一个生成jwt
的方法Crete
namespace JWTWebApplication.Services
{
public class AuthService
{
//通过依赖注入的方式,将配置参数带入到类中。后面会在program.cs文件中,进行注册。
private readonly JwtSettings _jwtSettings;
public AuthService(IOptions<JwtSettings> jwtSettings)
{
_jwtSettings = jwtSettings.Value;
}
/// <summary>
/// 用于令牌的生成
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public string Create(User user)
{
}
}
}
在Create
方法中,先实例化JwtSecurityTokenHandler
,它负责生成token
(令牌)
var handler = new JwtSecurityTokenHandler();
下一步是生成令牌的信息并进行签名,因此我们需要刚才配置信息的秘钥,并使用SigningCredentials
完成签名,SigningCredentials
需要两个参数:秘钥和算法
var privateKey = Encoding.UTF8.GetBytes(_jwtSettings.SecretKey);
var credentials = new SigningCredentials(
new SymmetricSecurityKey(privateKey),
SecurityAlgorithms.HmacSha256);
下一步,就是将我们需要加密到token
中的数据,通过方法实现。该如何添加呢?它的代码是这样的:
new Claim(ClaimTypes.Name, user.Username) //可以理解为键/值结构
为了简化操作,我将创建一个方法来返回我们将保存在令牌中的 ClaimsIdentity(所有令牌声明的列表),该方法会自动添加到令牌的有效负载中。
//准备给Token中的值
private static ClaimsIdentity GenerateClaims(User user)
{
var ci = new ClaimsIdentity();
ci.AddClaim(new Claim("id", user.Id.ToString()));
ci.AddClaim(new Claim(ClaimTypes.Name, user.Username));
ci.AddClaim(new Claim(ClaimTypes.GivenName, user.Name));
ci.AddClaim(new Claim(ClaimTypes.Email, user.Email));
foreach (var role in user.Roles)
ci.AddClaim(new Claim(ClaimTypes.Role, role));
return ci;
}
下一步操作是创建 SecurityTokenDescriptor 的实例,以便在令牌中包含基本信息
var tokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = credentials,
Expires = DateTime.UtcNow.AddMinutes(_jwtSettings.AccessTokenExpirationMinutes),
Subject = GenerateClaims(user)
};
然后我使用handler.CreateToken()
方法生成令牌,并且使用handler.WriteToken(token)
方法,将JwtSecurityToken
序列化为紧凑序列化格式JWT
并返回。
var token = handler.CreateToken(tokenDescriptor);
return handler.WriteToken(token);
最终,生成完成的代码如下:
using JWTWebApplication.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace JWTWebApplication.Services
{
public class AuthService
{
//通过依赖注入的方式,将配置参数带入到类中
private readonly JwtSettings _jwtSettings;
public AuthService(IOptions<JwtSettings> jwtSettings)
{
_jwtSettings = jwtSettings.Value;
}
/// <summary>
/// 用于令牌的生成
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public string Create(User user)
{
var handler = new JwtSecurityTokenHandler();
var privateKey = Encoding.UTF8.GetBytes(_jwtSettings.SecretKey);
var credentials = new SigningCredentials(
new SymmetricSecurityKey(privateKey),
SecurityAlgorithms.HmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = credentials,
Expires = DateTime.UtcNow.AddMinutes(_jwtSettings.AccessTokenExpirationMinutes),
Subject = GenerateClaims(user)
};
var token = handler.CreateToken(tokenDescriptor);
return handler.WriteToken(token);
}
//准备给Token中的值
private static ClaimsIdentity GenerateClaims(User user)
{
var ci = new ClaimsIdentity();
ci.AddClaim(new Claim("id", user.Id.ToString()));
ci.AddClaim(new Claim(ClaimTypes.Name, user.Username));
ci.AddClaim(new Claim(ClaimTypes.GivenName, user.Name));
ci.AddClaim(new Claim(ClaimTypes.Email, user.Email));
foreach (var role in user.Roles)
ci.AddClaim(new Claim(ClaimTypes.Role, role));
return ci;
}
}
}
有了生成token的方法,那我们就需要进行配置,准备使用了。具体来说,就是在program.cs
中进行配置
首先添加开启授权和认证功能的代码
builder.Services.AddAuthentication();//启用身份验证功能--认证主要是指,用户米、密码是否正确
builder.Services.AddAuthorization();//启用授权功能---授权主要是看有没有权限
app.UseAuthentication(); //启用认证功能
app.UseAuthorization(); //启用授权功能
其次,要指定使用 JWT 进行身份验证,需要进行配置调整。这涉及设置 DefaultChallengeScheme
以定义如何检查每个传入请求以确定适当的身份验证方法。这可确保应用程序收到的每个请求都被视为 JWT 身份验证
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
});
此外,可以通过 AddJwtBearer()
方法进行验证令牌。在本例中,将使用私钥,并且为了简单起见,排除了对颁发者和受众的验证
//收到的每个请求都被视为 JWT 身份验证
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
};
});
因为有些类,用到了依赖注入的方式实现,因此在配置文件中加了一下依赖注入的声明
//依赖注入
builder.Services.AddTransient<AuthService>();
builder.Services.AddOptions();
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("JwtSettings"));
因此,完成的配置文件如下:
using JWTWebApplication.Models;
using JWTWebApplication.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Options;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//依赖注入
builder.Services.AddTransient<AuthService>();
builder.Services.AddOptions();
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("JwtSettings"));
//启用功能
builder.Services.AddAuthentication();//启用身份验证功能--认证主要是指,用户米、密码是否正确
builder.Services.AddAuthorization();//启用授权功能---授权主要是看有没有权限
//读取配置文件的数据
//这儿自定义了一个JwtSettings类,类里面的属性要与配置文件中的一致
var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>();
//收到的每个请求都被视为 JWT 身份验证
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
};
});
//声明一个指定名称的认证策略
builder.Services.AddAuthorization(x =>
{
x.AddPolicy("rolePolicy", p => p.RequireRole("developer"));
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthentication(); //启用认证功能
app.UseAuthorization(); //启用授权功能
app.MapControllers();
app.Run();
至此,基本的配置已经完成,让我们开始使用吧
首先,先创建一个api的Controller
,并写下如下的代码
namespace JWTWebApplication.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class JWTTestController : ControllerBase
{
//依赖注入。在program文件中的依赖注入,这儿就用到了
private readonly AuthService _authService;
public JWTTestController(AuthService authService)
{
_authService = authService;
}
//具体的三个方法实现
}
}
在类里面,我们将会创建三个方法:Login
、test
、roletest
。这三个方法分别用于:根据传入的用户米/密码,生成token
。test
和roletest
分别用于测试权限认证和测试指定认证名称(就是在program
中的x.AddPolicy("rolePolicy", p => p.RequireRole("developer"));
),完整的代码如下:
namespace JWTWebApplication.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class JWTTestController : ControllerBase
{
//依赖注入
private readonly AuthService _authService;
public JWTTestController(AuthService authService)
{
_authService = authService;
}
[HttpGet]
public string Login(string username,string password)
{
//获取前台传过来的用户名、密码的数据
var user = new User();
user.Username = username;
user.Password = password;
//从后台进行身份验证,并获取该登录人的其他信息(例如,角色、邮箱等各类信息)
user.Name = "zhenglin";
user.Id = 1;
user.Email = "zhenglin@163.com";
user.Roles = new string[] { "developer" };
//返回token
return _authService.Create(user);
}
[Authorize]
[HttpGet]
public StatusCodeResult test()
{
return StatusCode(200);
}
[Authorize("rolePolicy")]
[HttpGet]
public StatusCodeResult roletest()
{
return StatusCode(200);
}
}
}
至此,全部代码完成,准备验证
首先测试一下Login
,看能否返回token
其次,测试一下携带
token
的test
方法
最后测试一下,指定名称的认证
3、代码下载
代码还在审核中,待审核后,将免费下载