数字人全息舱怎么选?2025年5大核心选购指南
2026/5/7 2:49:20
答案:SHA256 是摘要算法,速度太快,容易被暴力破解。企业必须用 BCrypt / Argon2(自适应哈希,慢、加盐、防暴力破解)。
答案:不需要,BCrypt自动生成盐、自动存在哈希里,验证时自动提取。
存非敏感信息:UserId、Account、RoleIds、RoleCodes绝对不存密码。
API # 接口、Controller Application # 用例、Service、DTO、VO Domain # 实体、仓储接口 Infrastructure # 技术实现:JWT、BCrypt、仓储实现、EFInfrastructure/ Commons/ Jwt/ JwtService.cs ✅ JWT 服务 Security/ BCryptUtil.cs ✅ 密码加密Application/ Services/ UserService.cs ✅ 写登录逻辑BCrypt.Net-Next Microsoft.IdentityModel.Tokens System.IdentityModel.Tokens.Jwtusing BCrypt.Net; namespace Admin.NET.Infrastructure.Commons.Security; public static class BCryptUtil { /// <summary> /// 加密密码 /// </summary> public static string HashPassword(string password) { return BCrypt.Net.BCrypt.HashPassword(password); } /// <summary> /// 验证密码 /// </summary> public static bool Verify(string password, string hash) { return BCrypt.Net.BCrypt.Verify(password, hash); } }using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Admin.NET.Domain.Entities; using Microsoft.IdentityModel.Tokens; namespace Admin.NET.Infrastructure.Commons.Jwt; public class JwtService { private readonly string _secret; private readonly string _issuer; private readonly string _audience; private readonly int _expireMinutes; public JwtService(string secret, string issuer, string audience, int expireMinutes) { _secret = secret; _issuer = issuer; _audience = audience; _expireMinutes = expireMinutes; } public string GenerateToken(User user) { var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Account), new Claim("UserId", user.Id.ToString()) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _issuer, audience: _audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_expireMinutes), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); } /// <summary> /// 校验Token /// </summary> public bool ValidateToken(string token, out ClaimsPrincipal? claims) { claims = null; try { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret)); var validateParam = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = _issuer, ValidAudience = _audience, IssuerSigningKey = key, ClockSkew = TimeSpan.Zero }; var handler = new JwtSecurityTokenHandler(); claims = handler.ValidateToken(token, validateParam, out _); return true; } catch { return false; } } }"Jwt": { "Secret": "ABCDEFG123456789ABCDEFG123456789", "Issuer": "AdminWeb", "Audience": "AdminClient", "ExpireMinutes": 120 }// 读取配置 var jwtSecret = builder.Configuration["Jwt:Secret"]; var jwtIssuer = builder.Configuration["Jwt:Issuer"]; var jwtAudience = builder.Configuration["Jwt:Audience"]; var jwtExpire = int.Parse(builder.Configuration["Jwt:ExpireMinutes"]!); // 注册 JWT 服务 builder.Services.AddSingleton(new JwtService(jwtSecret, jwtIssuer, jwtAudience, jwtExpire));using Admin.NET.Application.Dtos; using Admin.NET.Application.Vos; using Admin.NET.Infrastructure.Commons.Jwt; using Admin.NET.Infrastructure.Commons.Security; public async Task<R<LoginVo>> LoginAsync(LoginDto dto) { // 1. 查询用户 var user = await _uow.UserRepository.GetUserByAccountAsync(dto.Account); if (user == null) return R<LoginVo>.Fail("账号或密码错误"); // 2. 校验密码(BCrypt) if (!BCryptUtil.Verify(dto.Password, user.Password)) return R<LoginVo>.Fail("账号或密码错误"); // 3. 生成 JWT Token var token = _jwtService.GenerateToken(user); // 4. 组装返回 VO var vo = new LoginVo { Token = token, UserId = user.Id, Account = user.Account, Name = user.Name }; return R<LoginVo>.Success(vo); }[HttpPost("login")] public async Task<ActionResult<R<LoginVo>>> Login([FromBody] LoginDto dto) { return await _userService.LoginAsync(dto); }// 新增用户时 user.Password = BCryptUtil.HashPassword(dto.Password);