Live2D 看板娘 / Demo

webapi授权认证

webapi授权认证

一、需要类包

  • Microsoft.AspNetCore.Authentication.JwtBearer

二、相关名词

Authentication(认证):标识用户的身份,一般发生在登录的时候。

Authorization(授权):授予用户权限,指定用户能访问哪些资源;授权的前提是知道这个用户是谁,所以授权必须在认证之后。

三、认证

3.1 基本步骤

1.安装相关 Nuget 包:Microsoft.AspNetCore.Authentication.JwtBearer
2.准备配置信息(密钥等)
3.注册服务
4.调用中间件
5.实现一个 JwtHelper,用于生成 Token
6.控制器限制访问(添加 Authorize 标签)

3.2 appsetting.json配置信息

 // token配置
 "JwtConfig": {
   "SecretKey": "zhouxxxxxx@qq.email", // 密钥
   //"Issuer": "zhouBing", // 颁发单位
   "Issuer": "https://www.baidu.com/", // 颁发单位
   //"Audience": "http://localhost:8080", // 接收单位
   "Audience": "https://www.baidu.com/", // 接收单位
   "Expired": 30, // 过期时间(单位min)30min
   "RefreshTokenExpired": 60, // 刷新Token 60min
   "NotBefore": "", // 有效期自
   "Expiration": "", // 到期时间
   "SigningKey": "", // 签名密钥
   "SigningCredentials": "" // 签名凭证
 },

3.3 注册服务和调用中间件

//引入所需的命名空间
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

// 将 JwtHelper 的创建交由 DI 容器
builder.Services.AddSingleton(new GenerateJwt(_configuration, _dbContext));

JwtInfo jwtInfo = new JwtInfo();
builder.Configuration.Bind("JwtConfig", jwtInfo);


builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // 默认 Bearer JwtBearerDefaults.AuthenticationScheme
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuer = true,//是否验证 Issuer(发行商)
                ValidateAudience = true,//是否验证 Audience(受众者)
                ValidateLifetime = true,//是否验证失效时间
                ValidateIssuerSigningKey = true,//是否验证 Issuer 的签名键
                ValidAudience = jwtInfo.Audience, //此处应该是IdentityServer的地址
                ValidIssuer = jwtInfo.Issuer,// ValidAudience,ValidIssuer这两项的值要和验证中心的只保持一致。
                ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtInfo.SecretKey!))
            };
        });

//调用中间件:UseAuthentication(认证),必须在所有需要身份认证的中间件前调用,比如 UseAuthorization(授权)。
app.UseAuthentication();
app.UseAuthorization();

JwtInfo类型

 public class JwtInfo:BaseEntitys
 {
     public Guid Id { get; set; }
     public string TokenId { get; set; } = null!;
     //public JwtInfo Value => this;
     [DisplayName("密钥")]
     public string? SecretKey { get; set; } = null!;// 密钥
     [DisplayName("颁发者")]
     public string? Issuer { get; set; } // 颁发者
     [DisplayName("接收者")]
     public string? Audience { get; set; } // 接收者
     [DisplayName("过期时间")]
     public int Expired { get; set; }// 过期时间
     [DisplayName("上次颁发时间")]
     public DateTime NotBefore { get; set; } // 上次颁发时间
     [DisplayName("发布时间")]
     public DateTime IssuedAt { get; set; } // 发布时间
     [DisplayName("到期时间")]
     public DateTime Expiration { get; set; } // = IssuedAt.AddMinutes(Expired);// 到期时间
     [DisplayName("签名密钥")]
     public string? SigningKey { get; set; } = null!;// 签名密钥
     //public static SecurityKey? SigningKey { get; set; } = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));// 签名密钥
     [DisplayName("签名凭证")]
     public string? SigningCredentials { get; set; } // 签名凭证
     //public SigningCredentials? SigningCredentials { get; set; } = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);// 签名凭证
 }

3.4 JwtHelper 类实现

public class GenerateJwt
{
    public JwtInfoDto _jwtConfig;
    public readonly IConfiguration _configuration;
    // public readonly MysqlDbContext _dbContext;
    public GenerateJwt(IConfiguration configuration)z
    // public GenerateJwt(IConfiguration configuration, MysqlDbContext dbContext)
    {
        _configuration = configuration;
        _jwtConfig = _configuration.GetSection("JwtConfig").Get<JwtInfoDto>();
        // _dbContext = dbContext;
    }
    /// <summary>
    /// 生成token
    /// </summary>
    /// <param name="sub"></param>
    /// <param name="customClaims">携带的用户信息</param>
    /// <returns></returns>
    public JwtTokenResult GenerateEncodedTokenAsync(string sub, LoginUserModel customClaims)
    {
        //创建用户身份标识,可按需要添加更多信息
        var claims = new List<Claim>
        {
            //new Claim("Userid", customClaims.Userid),
            new Claim("Username", customClaims.Username),
            new Claim("Realname",customClaims.Realname),
            new Claim("Roles", customClaims.Roles),
            new Claim("Permissions", customClaims.Permissions),
            new Claim("NormalPermissions", customClaims.NormalPermissions),
            new Claim(JwtRegisteredClaimNames.Sub, sub),
        };

        DateTime dateNow = DateTime.Now;
        var authSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtConfig:SecretKey"]));
        //创建令牌
        var jwt = new JwtSecurityToken(
            issuer: _jwtConfig.Issuer,
            audience: _jwtConfig.Audience,
            claims: claims,
            //notBefore: _jwtConfig.NotBefore,
            notBefore: dateNow,
            //expires: _jwtConfig.Expiration,
            expires: dateNow.AddMinutes(_jwtConfig.Expired),
            //signingCredentials: null, // 签名凭证
            signingCredentials: new SigningCredentials(authSecurityKey, SecurityAlgorithms.HmacSha256) // 签名凭证
            );
        string access_token = new JwtSecurityTokenHandler().WriteToken(jwt);
        string refresh_Token = CreateRefreshTokenBase64();

        return new JwtTokenResult()
        {
            Access_token = access_token,
            Expires_in = dateNow.AddMinutes(_jwtConfig.Expired), // 过期时间
            //Expires_in = _jwtConfig.Expired * 60, // 过期时间(秒)
            Token_type = JwtBearerDefaults.AuthenticationScheme,
            Refresh_token = refresh_Token,
            User = customClaims,
            SigningCredentials = jwt.SigningCredentials
        };
    }

    /// <summary>
    /// 简单的生成RefreshToken
    /// </summary>
    /// <param name="access_token"></param>
    /// <param name="refresh_Token"></param>
    /// <returns></returns>
    public string CreateRefreshTokenBase64()
    {
        // 创建一个随机的Token用做Refresh Token
        var randomNumber = new byte[32];

        using var rng = RandomNumberGenerator.Create();
        rng.GetBytes(randomNumber);

        return Convert.ToBase64String(randomNumber);
    }

    /// <summary>
    /// 生成RefreshToken
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    /// <exception cref="SecurityTokenException"></exception>
    public string CreateRefreshToken(string UserId)
    {
        GetDbContext dbContext = new GetDbContext(_configuration);
        var _dbContext = dbContext.GetContext();
        var context = new MysqlDbContext(_dbContext);
        if (!context.UserToken.Where(x => x.UserId.Equals(UserId) && x.Refresh_token == null).Any())
        {
            return CreateRefreshTokenBase64();
        }
        return "";
    }

    /// <summary>
    /// 根据过期Token生成新的Token
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    /// <exception cref="SecurityTokenException"></exception>
    public bool GetPrincipalFromExpiredToken(string token)
    {
        // 根据已过期的Token获取用户相关的Principal数据,用来生成新的Token
        var jwtSettings = _configuration.GetSection("JwtConfig");
        var authSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtConfig:SecretKey"]));
        try
        {
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = true,
                ValidateIssuer = true,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = authSecurityKey,
                ValidateLifetime = true,
                ValidIssuer = jwtSettings["Issuer"],
                ValidAudience = jwtSettings["Audience"],
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var securityToken);
            if (securityToken is not JwtSecurityToken jwtSecurityToken ||
                !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
            {
                return false;
            }
        }
        catch(Exception ex)
        {
            return false;
        }
        

        return true;
    }
}


四、测试

4.1 操作步骤

1. 生成Token
2. 携带Token访问接口

控制器

[ApiController]
[Route("[controller]/[action]")]
public class UserController : ControllerBase
{
    private string connectionString;

    private IConfiguration _configuration;
    private readonly JwtInfo _config;
    private IServiceProvider _serviceProvider;
    private readonly MysqlDbContext _dbContext;

    //private IdentifityApp _IdentifityApp;
    //private IConnectionFactory _IConnectionFactory;
    public UserController(IConfiguration configuration, IOptions<JwtInfo> config, IServiceProvider serviceProvider, MysqlDbContext dbContext)
    {
        _configuration = configuration;
        _config = config.Value;
        //_IConnectionFactory = iConnectionFactory;
        _serviceProvider = serviceProvider;
        //_IdentifityApp=identifityApp;
        connectionString = _configuration.GetConnectionString("MySQLConnection");
        _dbContext = dbContext;
    }

    // 生成Token
    //[Authorize(Roles = "Administrators")]
    [Authorize(Roles= "admin")]
    [HttpPost]
    public JsonResult GetAuthority()
    {
        //using (var cnn = _IConnectionFactory.Connection())
        //{
        string result = "";
        List<Authority> list = new List<Authority>();
        using (MySqlConnection connection = new MySqlConnection(_configuration.GetConnectionString("MySQLConnection"))) {
            string sql = "select * from Authority";
            MySqlCommand command = new MySqlCommand(sql, connection);
            connection.Open();
            using (MySqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    list.Add(new Authority
                    {
                        Id = reader.GetInt16("Id"),
                        AuthorityName = reader.GetString("AuthorityName"),
                    });
                }
            }
        }
        JsonResult json = new JsonResult(new
        {
            code = 200,
            msg = "成功",
            resule = list
        });
        return json;
        //}

    }

    [HttpPost]
    public JsonResult GenerateToken()
    {
        LoginUserModel loginUserModel = new LoginUserModel();
        //loginUserModel.Userid = "08db7530-382f-42bf-828c-32dd169ac2d9";
        loginUserModel.Username = "admin";
        loginUserModel.Realname = "zxx";
        loginUserModel.Roles = "admin";
        loginUserModel.Permissions = "1";
        loginUserModel.NormalPermissions = "2";

        GenerateJwt generateJwt = new GenerateJwt(_configuration, _dbContext);
        JwtTokenResult JwtInfo = generateJwt.GenerateEncodedTokenAsync("Sub", loginUserModel);

        JsonResult json = new JsonResult(new
        {
            code = "200",
            msg = "成功",
            result = JwtInfo
        });

        return json;
    }

    [HttpGet]
    //[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    [Authorize]
    public object GetData()
    {
        return new
        {
            Id = 1,
            Name = "Tom"
        };
    }
}

4.2 Postman测试

4.2.1 获取token

4.2.2 调用权限接口

posted @ 2024-01-17 14:57  土豆煮石头  阅读(7)  评论(0编辑  收藏  举报