- Backend: .NET 10 Minimal API + EF Core + PostgreSQL - Frontend: Flutter + Riverpod + GoRouter + Dio - AI: DeepSeek LLM + Qwen VLM (OpenAI-compatible) - Auth: SMS + JWT (access/refresh tokens) - Features: AI chat, health tracking, medication management, diet analysis, exercise plans, doctor consultations, report analysis
81 lines
2.5 KiB
C#
81 lines
2.5 KiB
C#
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.IdentityModel.Tokens;
|
||
using System.IdentityModel.Tokens.Jwt;
|
||
using System.Security.Claims;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
|
||
namespace Health.Infrastructure.Services;
|
||
|
||
/// <summary>
|
||
/// JWT Token 生成与验证服务
|
||
/// </summary>
|
||
public sealed class JwtProvider
|
||
{
|
||
private readonly string _secret;
|
||
private readonly string _issuer;
|
||
private readonly string _audience;
|
||
|
||
public JwtProvider(IConfiguration configuration)
|
||
{
|
||
_secret = configuration["JWT_SECRET"] ?? "dev-secret-key-change-in-production-min-32-chars!!";
|
||
_issuer = configuration["JWT_ISSUER"] ?? "health-manager";
|
||
_audience = configuration["JWT_AUDIENCE"] ?? "health-manager-app";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成 access_token(30 分钟有效)
|
||
/// </summary>
|
||
public string GenerateAccessToken(Guid userId, string phone)
|
||
{
|
||
var claims = new[]
|
||
{
|
||
new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
|
||
new Claim(ClaimTypes.MobilePhone, phone),
|
||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||
};
|
||
|
||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret));
|
||
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||
|
||
var token = new JwtSecurityToken(
|
||
issuer: _issuer,
|
||
audience: _audience,
|
||
claims: claims,
|
||
expires: DateTime.UtcNow.AddMinutes(30),
|
||
signingCredentials: credentials
|
||
);
|
||
|
||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成 refresh_token(30 天有效)
|
||
/// </summary>
|
||
public string GenerateRefreshToken()
|
||
{
|
||
var randomBytes = new byte[64];
|
||
using var rng = RandomNumberGenerator.Create();
|
||
rng.GetBytes(randomBytes);
|
||
return Convert.ToBase64String(randomBytes);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证 JWT token 并返回 ClaimsPrincipal
|
||
/// </summary>
|
||
public TokenValidationParameters GetValidationParameters()
|
||
{
|
||
return new TokenValidationParameters
|
||
{
|
||
ValidateIssuer = true,
|
||
ValidateAudience = true,
|
||
ValidateLifetime = true,
|
||
ValidateIssuerSigningKey = true,
|
||
ValidIssuer = _issuer,
|
||
ValidAudience = _audience,
|
||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret)),
|
||
ClockSkew = TimeSpan.Zero
|
||
};
|
||
}
|
||
}
|