在当今的软件开发中,安全性是至关重要的。为了确保应用程序的资源受到保护,并只对经过身份验证和授权的用户开放,我们经常使用JSON Web Token (JWT)来进行权限验证。在.NET 6中,实现这样的系统变得更加简单和直接。
一、JWT简介
JWT是一种开放标准,它定义了一种紧凑的、自包含的方式来在各方之间传递信息。它经常被用于身份验证和授权,因为它提供了一种在不需要中心化身份验证系统的情况下,在客户端和服务器之间安全地传输用户信息的方法。
二、在.NET 6中实现JWT验证
在.NET 6项目中实现基于JWT的权限验证通常涉及以下步骤:
- 创建JWT认证服务:这个服务将负责生成和验证JWT。它需要一个密钥,该密钥用于签名令牌,以便之后可以验证它。
- 用户管理:创建一个服务来处理用户管理任务,例如创建、查询和验证用户凭据。这通常是与数据库交互的代码。
- 配置依赖注入:如果使用如ASP.NET Core的依赖注入框架,需要将认证服务和其他相关组件注册到容器中,以便在整个应用程序中重用它们。
- 使用权限验证服务:在控制器或动作方法上使用
[Authorize]
属性来要求JWT验证。如果令牌无效或过期,用户将被拒绝访问受保护的资源。
三、注意事项
- 密钥管理:确保密钥的安全存储和更新。如果密钥泄露,任何人都可以生成有效的JWT,可能导致安全风险。
- 令牌过期时间:合理设置令牌的过期时间,以平衡用户便利性和安全性。
- 错误处理:当验证失败时,提供适当的错误响应,帮助客户端理解发生了什么。
- 测试和调试:确保对整个流程进行彻底的测试,以确保没有安全漏洞或问题。
四、结论
使用JWT进行权限验证是保护.NET 6应用程序的一种有效方法。通过遵循最佳实践和密切关注安全更新,可以确保应用程序的安全性。
正好在最近需要写一个使用jwt鉴权的功能,下面是代码。
先新建一个配置类,用于接收和传递jwt对象:
public class JwtOpt
{
public string Audience { get; set; } = string.Empty;
public string Issuer { get; set; } = string.Empty;
public string SecurityKey { get; set; } = string.Empty;
}
在appsettings.json文件存储相关信息:
"JwtOpt": {
"Audience": "***",
"Issuer": "***",
"SecurityKey": "************************************"
}
写一个JwtMiddleware作为中间件:
public class JwtMiddleware
{
private readonly RequestDelegate _next;
private readonly JwtOpt _authOptions;
public JwtMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_authOptions = configuration.GetSection("JwtOpt").Get<JwtOpt>();
}
public async Task Invoke(HttpContext context)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last()
?? context.Request.Headers["token"].FirstOrDefault()
?? context.Request.Query["Token"].FirstOrDefault()
?? context.Request.Cookies["Token"];
if (token != null)
AttachUserToContext(context, token);
await _next(context);
}
private void AttachUserToContext(HttpContext context, string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
byte[]? key = Encoding.ASCII.GetBytes(_authOptions.SecurityKey);
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = _authOptions.Issuer,
ValidAudience = _authOptions.Audience,
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken;
var user = jwtToken.Claims.First(x => x.Type == "User").Value;
var claimsIdentity = new ClaimsIdentity(new Claim[] { new Claim("User", jwtToken.Claims.First(x => x.Type == "User").Value) });
Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity);
context.Items["User"] = user;
}
catch
{
}
}
}
写一个ApiAuthorizeAttribute用于验证:
public class ApiAuthorizeAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.Items["User"];
if (HasAllowAnonymous(context) == false && user == null)
{
context.Result = new JsonResult(new { msg = "你无权做这些", code = 401 })
{ StatusCode = StatusCodes.Status401Unauthorized };
}
}
private static bool HasAllowAnonymous(AuthorizationFilterContext context)
{
var filters = context.Filters;
if (filters.OfType<IAllowAnonymousFilter>().Any())
{
return true;
}
var endpoint = context.HttpContext.GetEndpoint();
return endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null;
}
}
在Program.cs中加入代码:
builder.Services.AddControllers()
.AddInject().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
}).AddMvcOptions((opt) => { opt.Filters.Add<ApiAuthorizeAttribute>(); });
app.UseMiddleware<JwtMiddleware>();
在登录认证成功后生成token并返回:
IConfiguration _configuration;
private AutoDingContext _ctx;
public TestController( IConfiguration configuration)
{
_configuration = configuration;
}
//以上是依赖注入
var jwtOpt = _configuration.GetSection("JwtOpt").Get<JwtOpt>(); //获取配置文件里JwtOpt的信息
var claims = new[]
{
new Claim(ClaimTypes.Name, username),
new Claim("User",username)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOpt.SecurityKey));
var signMethod = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenOpt = new JwtSecurityToken(
issuer: jwtOpt.Issuer,
audience: jwtOpt.Audience,
claims: claims,
expires: DateTime.Now.AddDays(7),
signingCredentials: signMethod
);
var tokenStr = new JwtSecurityTokenHandler().WriteToken(tokenOpt); //生成的token
这样功能就完成了,下面是测试返回的结果:
未进行token认证时:
登录获取token:
在请求时加上token:
测试完美通过。使用JWT进行权限验证是保护.NET 6应用程序的一种有效方法。通过遵循最佳实践和密切关注安全更新,可以确保应用程序的安全性。
最后向大家介绍一下在开发中我使用的后端框架----Fruion,让 .NET 开发更简单,更通用,更流行。官网地址如下:
让 .NET 开发更简单,更通用,更流行。 Furion | Furion
是一个非常不错的框架,上手快速,对新手也很友好,官方文档也很详细,使.net开发更加方便快捷。可惜有一部分文档需要收费了,大家可以支持一下开源作者。