|
主题设置

卡片式面板通常用于非白色背景色的主体内

Token 验证

Web API 的 Token 验证是一种用于保护 API 端点的认证和授权机制,它主要通过生成和验证令牌来确保只有授权用户可以访问特定资源。

JwtBearer 的安装和配置

在 API 项目中安装 JwtBearer 库:Microsoft.AspNetCore.Authentication.JwtBearer

appsettings.json 中添加 JwtConfig 的配置

"JwtConfig": {
    "SecretKey": "123456789123456789123456789", // 密钥可以是 guid, 也可以是随便一个字符串
    "Issuer": "张三", // 颁发者
    "Audience": "张三", // 接收者
    "Expired": 30 // 过期时间(30min)
}

在 API 项目中,创建 Jwt 文件夹,同时创建 JwtConfig 类。

public class JwtConfig
{
    /// <summary>
    /// 密钥
    /// </summary>
    public string SecretKey { get; set; }

    /// <summary>
    /// 发布者
    /// </summary>
    public string Issuer { get; set; }

    /// <summary>
    /// 接受者
    /// </summary>
    public string Audience { get; set; }

    /// <summary>
    /// 过期时间(min)
    /// </summary>
    public int Expired { get; set; }

    /// <summary>
    /// 生效时间
    /// </summary>
    public DateTime NotBefore => DateTime.Now;

    /// <summary>
    /// 过期时间
    /// </summary>
    public DateTime Expiration => DateTime.Now.AddMinutes(Expired);

    /// <summary>
    /// 密钥Bytes
    /// </summary>
    private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));

    /// <summary>
    /// 加密后的密钥,使用HmacSha256加密
    /// </summary>
    public SigningCredentials SigningCredentials =>
        new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);

    /// <summary>
    /// 认证使用的密钥
    /// </summary>
    public SymmetricSecurityKey SymmetricSecurityKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
}

在 Jwt 文件夹中继续创建 JwtHelper 类。

public class JwtHelper
{
    public JwtConfig JwtConfig { get; set; }

    public JwtHelper()
    {

    }

    /// <summary>
    /// 添加Jwt服务
    /// </summary>
    public void AddJwtService(IServiceCollection services)
    {
        services.AddAuthentication(option =>
        {
            //认证middleware配置
            option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                //Token颁发机构
                ValidIssuer = JwtConfig.Issuer,
                //颁发给谁
                ValidAudience = JwtConfig.Audience,
                //这里的key要进行加密
                IssuerSigningKey = JwtConfig.SymmetricSecurityKey,
                //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                ValidateLifetime = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateIssuerSigningKey = true,
                RequireExpirationTime = true,
                ClockSkew = TimeSpan.FromSeconds(30) // 缓冲时间,默认5分钟(因为服务器与客户端的时间可能不同步,所以需要设置一个缓冲时间,最终总过期时间=过期时间+缓冲时间)
            };
        });
    }

    /// <summary>
    /// 返回
    /// </summary>
    /// <returns></returns>
    public string GetJwtToken()
    {
        var claims = new List<Claim>();
        var jwtSecurityToken = new JwtSecurityToken(
                JwtConfig.Issuer, // 发布者
                JwtConfig.Audience, // 接受者
                claims,
                JwtConfig.NotBefore, // 生效时间
                JwtConfig.Expiration, // 过期时间
                JwtConfig.SigningCredentials // 加密后的密钥
            );
        var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
        token = "Bearer " + token; // 为了更简单的使用 Token,直接把返回的 Token 自带[Bearer ]这个前缀
        return token;
    }

    /// <summary>
    /// 返回
    /// </summary>
    /// <returns></returns>
    public string GetJwtToken(List<Claim> claims)
    {
        var jwtSecurityToken = new JwtSecurityToken(
                JwtConfig.Issuer, // 发布者
                JwtConfig.Audience, // 接受者
                claims, 
                JwtConfig.NotBefore, // 生效时间
                JwtConfig.Expiration, // 过期时间
                JwtConfig.SigningCredentials // 加密后的密钥
            );
        var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
        token = "Bearer " + token; // 为了更简单的使用 Token,直接把返回的 Token 自带[Bearer ]这个前缀
        return token;
    }

    /// <summary>
    /// UserModel类Token
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public string GetJwtToken(TUser user)
    {
        var claims = new List<Claim>() {
            new Claim("UserId",user.id.ToString()),
            new Claim("UserCode",user.code),
            new Claim("UserName",user.name),
            new Claim("UserRoleId",user.role_id.ToString()),
        };
        return GetJwtToken(claims);
    }

    /// <summary>
    /// 获取 Jwt 的信息
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public IEnumerable<Claim> Decode(HttpRequest request)
    {

        var authorization = request.Headers["Authorization"].ToString();
        // 因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头
        var auth = authorization.Split(" ")[1];
        var handler = new JwtSecurityTokenHandler();
        //反解密,获取其中的Claims
        var payload = handler.ReadJwtToken(auth).Payload;
        var claims = payload.Claims;
        return claims;
    }

    /// <summary>
    /// 解析得到 User
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public TUser DecodeToUser(HttpRequest request)
    {
        var claims = Decode(request);
        var user = new TUser()
        {
            id = new Guid(claims.Where(t => t.Type == "UserId").First().Value),
            code = claims.Where(t => t.Type == "UserCode").First().Value,
            name = claims.Where(t => t.Type == "UserName").First().Value,
            role_id = new Guid(claims.Where(t => t.Type == "UserRoleId").First().Value)
        };
        return user;
    }
}

在 ServiceExtensions.cs 类中增加 Jwt 的服务。

/// <summary>
/// Jwt
/// </summary>
/// <param name="service"></param>
/// <param name="config"></param>
public static JwtHelper AddJwt(this IServiceCollection service, IConfiguration config)
{
    // 获取 Config
    var jwtConfig = new JwtConfig();
    config.Bind("JwtConfig", jwtConfig);
    var jwtHelper = new JwtHelper()
    {
        JwtConfig = jwtConfig
    };
    service.AddSingleton<JwtHelper>(jwtHelper);
    return jwtHelper;
}

在 Program.cs 中增加 Jwt 的引用。

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        // Add services to the container.
        builder.Services.AddControllers().AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
        });

        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        builder.Services.AddSwaggerExt();

        builder.Services.ConfigureCors();
        builder.Services.ConfigureMysqlContext(builder.Configuration);
        builder.Services.ConfigureRepositoryWrapper();
        builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
        JwtHelper jwtHelper = builder.Services.AddJwt(builder.Configuration);
        jwtHelper.AddJwtService(builder.Services);

        var app = builder.Build();

        // Configure the HTTP request pipeline.
        if (app.Environment.IsDevelopment())
        {
            app.UseSwagger();
            app.UseSwaggerUI();
        }

        app.UseCors("CorsPolicy");
        app.UseAuthentication(); // 要在授权之前认证,这个和 [Authorize] 特性有关
        app.UseAuthorization();
        app.MapControllers();
        app.Run();
    }
}

Token 获取和解析

在 TUserController 控制器上增加 [Authorize] 特性,该特性指定了控制器中的所有接口,都有 Jwt 认证。

若某个接口需要放开 Jwt 认证,可在接口上使用 [AllowAnonymous] 特性进行标记。

[Authorize] 特性也可单独使用在接口上,只对某个接口指定需要 Jwt 认证。

private JwtHelper jwtHelper;

// 构造函数注入
public TUserController(IRepositoryWrapper repositoryWrapper, ILogger<TUserController> logger,IMapper mapper, JwtHelper jwtHelper)
{
    _repositoryWrapper = repositoryWrapper;
    _logger = logger;
    _mapper = mapper;
    this.jwtHelper = jwtHelper;
}

定义用户登录方法

/// <summary>
/// 用户登录
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("login")]
public async Task<IActionResult> Login([FromQuery] TUserParameter userParameter)
{
    try
    {
        var user = await _repositoryWrapper.UserRepository.GetByCode(userParameter.code);
        if (user == null) return NotFound("用户不存在");
        if(userParameter.code.Equals(user.code) && MD5Encrypt.Encrypt(userParameter.pwd).Equals(user.pwd))
        {
            var token = jwtHelper.GetJwtToken(new TUser()
            {
                id = user.id,
                code = user.code,
                name = user.name,
                role_id = user.role_id
            });
            return Ok(token);
        }
        else
        {
            return Unauthorized("用户名或密码错误");
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex.Message);
        return StatusCode(500, ex.Message);
    }
}

启动 webapi 项目,调用登录方法获取 Token 。

以上步骤成功获取 Token,然后在 TUserController 控制器中增加查询所有用户的方法,同时解析传入的 Token 。

/// <summary>
/// 查询所有用户
/// </summary>
/// <returns></returns>
[HttpGet("getAll")]
public async Task<IActionResult> GetAll()
{
    try
    {
        var operateUser = jwtHelper.DecodeToUser(this.Request);
        var users = await _repositoryWrapper.UserRepository.GetAll();
        var result = _mapper.Map<IEnumerable<TUserDto>>(users);
        return Ok(result);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex.Message);
        return StatusCode(500,ex.Message);
    }
}

运行程序,先调用登录方法获取 Token 。

将 Token 添加到 Swagger 里面。

调用 “查询所有用户” 的方法,成功获取到 Token 中传递的信息。

参考内容