水果网站推广,wordpress 支持 手机,佛山网站到首页排名,企业所得税应纳税所得额怎么算目录
JWT缺点
方案
实现
Program.cs
IdentityHelper.cs
Controller
NotCheckJWTVersionAttribute.cs
JWTVersionCheckkFilter.cs
优化 JWT缺点
到期前#xff0c;令牌无法被提前撤回。什么情况下需要撤回#xff1f;用户被删除了、禁用了#xff1b;令牌被盗用了令牌无法被提前撤回。什么情况下需要撤回用户被删除了、禁用了令牌被盗用了单设备登录。需要JWT撤回的场景用传统Session更合适。如果需要在JWT中实现思路用Redis保存状态或者用refresh_tokenaccess_token机制等。
方案
在用户表中增加一个整数类型的列JWTVersion代表最后一次发放出去的令牌的版本号每次登录、发放令牌的时候都让JWTVersion的值自增同时将JWTVersion的值也放到JWT令牌的负载中当执行禁用用户、撤回用户的令牌等操作的时候把这个用户对应的JWTVersion列的值自增当服务器端收到客户端提交的JWT令牌后先把JWT令牌中的JWTVersion值和数据库中JWTVersion的值做一下比较如果JWT令牌中JWTVersion的值小于数据库中JWTVersion的值就说明这个JWT令牌过期了。
实现
为用户实体User类增加一个long类型的属性JWTVersion。 public class MyUser : IdentityUserlong
{public string? WeChatAccout { get; set; }public long JWTVersions { get; set; }
} 修改登录并发放令牌的代码把用户的JWTVersion属性的值自增并且把JWTVersion的值写入JWT令牌。编写一个操作筛选器统一实现对所有的控制器的操作方法中JWT令牌的检查操作。把JWTValidationFilter注册到Program.cs中MVC的全局筛选器中。
Program.cs
using Identity框架;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Scalar.AspNetCore;
using System.Text;var builder WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
//将Bearer身份验证添加到Scalar
builder.Services.AddOpenApi(opt
{opt.AddDocumentTransformerBearerSecuritySchemeTransformer();
});
//添加数据库上下文
builder.Services.AddDbContextMyDbContext(opt
{string connStr Environment.GetEnvironmentVariable(ConnStr);opt.UseSqlServer(connStr);
});
//添加Filter
builder.Services.ConfigureMvcOptions(opt
{opt.Filters.AddJWTVersionCheckFilter();//添加JWT版本检查ActionFilter
});
//添加Identity服务
builder.Services.AddDataProtection();
builder.Services.AddIdentityCoreMyUser(options
{//设置密码规则不需要数字小写字母大写字母特殊字符长度为6options.Lockout.DefaultLockoutTimeSpan TimeSpan.FromSeconds(30);options.Password.RequireDigit false;options.Password.RequireLowercase false;options.Password.RequireUppercase false;options.Password.RequireNonAlphanumeric false;options.Password.RequiredLength 6;options.Tokens.PasswordResetTokenProvider TokenOptions.DefaultEmailProvider;options.Tokens.EmailConfirmationTokenProvider TokenOptions.DefaultEmailProvider;
});
var idBuilder new IdentityBuilder(typeof(MyUser), typeof(MyRole), builder.Services);
idBuilder.AddEntityFrameworkStoresMyDbContext().AddDefaultTokenProviders().AddRoleManagerRoleManagerMyRole().AddUserManagerUserManagerMyUser();
//添加JWT设置
builder.Services.ConfigureJWTSettings(builder.Configuration.GetSection(JWT));
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(opt
{var jwtOpt builder.Configuration.GetSection(JWT).GetJWTSettings();byte[] key Encoding.UTF8.GetBytes(jwtOpt.SecKey);//设置对称秘钥var secKey new SymmetricSecurityKey(key);//设置验证参数opt.TokenValidationParameters new(){ValidateIssuer false,//是否验证颁发者ValidateAudience false,//是否验证订阅者ValidateLifetime true,//是否验证生命周期ValidateIssuerSigningKey true,//是否验证签名IssuerSigningKey secKey//签名秘钥};
});var app builder.Build();
if (app.Environment.IsDevelopment())
{app.MapOpenApi();app.MapScalarApiReference();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();IdentityHelper.cs
public static class IdentityHelper
{public static async Task CheckAsync(this TaskIdentityResult task){var r await task;if (!r.Succeeded){throw new Exception(JsonSerializer.Serialize(r.Errors));}}
}
Controller
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;namespace Identity框架.Controllers
{[Route(api/[controller]/[action])][ApiController]public class DemoController : ControllerBase{private readonly UserManagerMyUser userManager;private readonly RoleManagerMyRole roleManager;private readonly IOptionsSnapshotJWTSettings jwtSettingsOpt;public DemoContrller(UserManagerMyUser userManager, RoleManagerMyRole roleManager, IOptionsSnapshotJWTSettings jwtSettingsOpt){this.userManager userManager;this.roleManager roleManager;this.jwtSettingsOpt jwtSettingsOpt;}[HttpPost][NotCheckJWTVersion]public async TaskActionResultstring Login(string userName, string password){//根据用户名查找用户var user await userManager.FindByNameAsync(userName);if (user null){return BadRequest(用户或密码错误1);}//判断是否登录成功失败则记录失败次数if (await userManager.CheckPasswordAsync(user, password)){//登录成功重置失败次数CheckAsync判断操作是否成功失败则抛出异常await userManager.ResetAccessFailedCountAsync(user).CheckAsync();//更新JWT版本号防止旧JWT被使用user.JWTVersions;await userManager.UpdateAsync(user);//身份验证声明ListClaim claims new ListClaim{new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),new Claim(ClaimTypes.Name, user.UserName),new Claim(JWTVersions, user.JWTVersions.ToString())};//获取用户角色添加到声明中var roles await userManager.GetRolesAsync(user);foreach (var role in roles){claims.Add(new Claim(ClaimTypes.Role, role));}//生成JWTstring key jwtSettingsOpt.Value.SecKey;DateTime expires DateTime.Now.AddSeconds(jwtSettingsOpt.Value.ExpireSeconds);byte[] keyBytes Encoding.UTF8.GetBytes(key);var secKey new SymmetricSecurityKey(keyBytes);var credentials new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);var tokenDescriptor new JwtSecurityToken(claims: claims,//声明expires: expires,//过期时间signingCredentials: credentials//签名凭据);string jwt new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);return jwt;}else{await userManager.AccessFailedAsync(user).CheckAsync();return BadRequest(用户或密码错误2);}}}
}
NotCheckJWTVersionAttribute.cs
[AttributeUsage(AttributeTargets.Method)]
public class NotCheckJWTVersionAttribute:Attribute
{
}
JWTVersionCheckkFilter.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Security.Claims;namespace Identity框架
{public class JWTVersionCheckFilter : IAsyncActionFilter{private readonly UserManagerMyUser userManager;public JWTVersionCheckFilter(UserManagerMyUser userManager){this.userManager userManager;}public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){//获取当前Action的特性判断是否有NotCheckJWTVersionAttribute特性,如果有则不检查JWTVersionControllerActionDescriptor controllerActionDescriptor context.ActionDescriptor as ControllerActionDescriptor;if (controllerActionDescriptor null){await next();return;}if (controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(NotCheckJWTVersionAttribute), true).Any()){await next();return;}//获取JWTVersion不存在则返回400var claimJWTVersion context.HttpContext.User.FindFirst(JWTVersions);if (claimJWTVersion null){context.Result new ObjectResult(payload中JWTVersion不存在){StatusCode 400};return;}//获取用户id不存在则返回400var claimUserId context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);long userId Convert.ToInt64(claimUserId.Value);//不用每次都查询数据库可以从缓存中获取用户var user await userManager.FindByIdAsync(userId.ToString());if (user null){context.Result new ObjectResult(用户不存在){StatusCode 400};return;}//判断JWTVersion是否过时long jwtVersionClient Convert.ToInt64(claimJWTVersion.Value);if (user.JWTVersions jwtVersionClient){context.Result new ObjectResult(客户端jwt过时){StatusCode 400};return;}await next();}}
}优化
每一次客户端和Controller的交互的时候检查JWTVersion的筛选器都要查询数据库性能太低可以用缓存进行优化。