一、今日核心内容
- 密码加密(BCrypt) ------ 企业绝对标准,不能明文存密码
- 登录接口逻辑
- 登录成功 → 颁发 JWT Token
- 返回前端:Token + 用户信息(VO)
- 全部写在 DDD 四层架构 内
二、高频面试题 + 答案
1. 密码为什么不能用 SHA256?要用什么?
答案 :SHA256 是摘要算法,速度太快,容易被暴力破解。企业必须用 BCrypt / Argon2(自适应哈希,慢、加盐、防暴力破解)。
2. BCrypt 需要自己维护盐值吗?
答案 :不需要,BCrypt 自动生成盐、自动存在哈希里,验证时自动提取。
3. 登录流程是什么?
- 接收账号密码
- 查询用户
- BCrypt 验证密码
- 生成 JWT Token
- 返回 Token
4. JWT 存什么?
存非敏感信息 :UserId、Account、RoleIds、RoleCodes绝对不存密码。
三、项目结构(严格 DDD 四层)
API # 接口、Controller
Application # 用例、Service、DTO、VO
Domain # 实体、仓储接口
Infrastructure # 技术实现:JWT、BCrypt、仓储实现、EF
今天所有代码都放在这 2 个位置:
1. 工具类(BCrypt + JWT)
Infrastructure/
Commons/
Jwt/
JwtService.cs ✅ JWT 服务
Security/
BCryptUtil.cs ✅ 密码加密
2. 登录逻辑
Application/
Services/
UserService.cs ✅ 写登录逻辑
四、第一步:安装包
BCrypt.Net-Next
Microsoft.IdentityModel.Tokens
System.IdentityModel.Tokens.Jwt
五、代码直接复制即用
1. Infrastructure/Commons/Security/BCryptUtil.cs
cs
using 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);
}
}
2. Infrastructure/Commons/Jwt/JwtService.cs
cs
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;
}
}
}
3. appsettings.json 配置
cs
"Jwt": {
"Secret": "ABCDEFG123456789ABCDEFG123456789",
"Issuer": "AdminWeb",
"Audience": "AdminClient",
"ExpireMinutes": 120
}
4. Program.cs 注册 JWT
cs
// 读取配置
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));
六、登录接口完整实现(你最关心)
Application/Services/UserService.cs
cs
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);
}
UserController 登录接口
cs
[HttpPost("login")]
public async Task<ActionResult<R<LoginVo>>> Login([FromBody] LoginDto dto)
{
return await _userService.LoginAsync(dto);
}
七、添加用户时密码必须加密
cs
// 新增用户时
user.Password = BCryptUtil.HashPassword(dto.Password);