Upgrade Nuget
大石头 authored at 2024-09-08 14:02:44
3.10 KiB
NewLife.Remoting
using System.Reflection;
using NewLife.Remoting.Extensions.Models;
using NewLife.Security;
using NewLife.Web;

namespace NewLife.Remoting.Extensions.Services;

/// <summary>令牌服务。颁发与验证令牌</summary>
/// <remarks>可重载覆盖功能逻辑</remarks>
public class TokenService
{
    private readonly ITokenSetting _tokenSetting;

    /// <summary>实例化</summary>
    /// <param name="tokenSetting"></param>
    public TokenService(ITokenSetting tokenSetting) => _tokenSetting = tokenSetting;

    private JwtBuilder GetJwt()
    {
        var ss = _tokenSetting.TokenSecret.Split(':');
        return new JwtBuilder
        {
            Algorithm = ss[0],
            Secret = ss[1],
        };
    }

    /// <summary>颁发令牌(使用ITokenSetting中的密钥)</summary>
    /// <param name="name"></param>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual TokenModel IssueToken(String name, String? id = null)
    {
        if (id.IsNullOrEmpty()) id = Rand.NextString(8);

        // 颁发令牌
        var jwt = GetJwt();
        jwt.Issuer = Assembly.GetEntryAssembly()?.GetName().Name;
        jwt.Subject = name;
        jwt.Id = id;
        jwt.Expire = DateTime.Now.AddSeconds(_tokenSetting.TokenExpire);

        return new TokenModel
        {
            AccessToken = jwt.Encode(null!),
            TokenType = jwt.Type ?? "JWT",
            ExpireIn = _tokenSetting.TokenExpire,
            RefreshToken = jwt.Encode(null!),
        };
    }

    /// <summary>验证并续发新令牌,过期前10分钟才能续发</summary>
    /// <param name="name"></param>
    /// <param name="token"></param>
    /// <returns></returns>
    public virtual TokenModel? ValidAndIssueToken(String name, String token)
    {
        if (token.IsNullOrEmpty()) return null;

        // 令牌有效期检查,10分钟内过期者,重新颁发令牌
        var jwt = GetJwt();
        if (!jwt.TryDecode(token, out _)) return null;

        return DateTime.Now.AddMinutes(10) > jwt.Expire ? IssueToken(name, jwt.Id) : null;
    }

    /// <summary>解码令牌</summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public virtual (JwtBuilder, Exception?) DecodeTokenWithError(String token)
    {
        if (token.IsNullOrEmpty()) throw new ArgumentNullException(nameof(token));

        // 解码令牌
        var jwt = GetJwt();

        Exception? ex = null;
        if (!jwt.TryDecode(token, out var message))
            ex = new ApiException(ApiCode.Forbidden, $"[{jwt.Subject}]非法访问 {message}");

        return (jwt, ex);
    }

    /// <summary>解码令牌,得到App应用</summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public virtual JwtBuilder DecodeToken(String token)
    {
        if (token.IsNullOrEmpty()) throw new ArgumentNullException(nameof(token));

        // 解码令牌
        var jwt = GetJwt();
        if (!jwt.TryDecode(token, out var message) || jwt.Subject.IsNullOrEmpty())
            throw new ApiException(ApiCode.Forbidden, $"非法访问[{jwt.Subject}],{message}");

        return jwt;
    }
}