搭建.net开发框架


前言

个人记录,仅供参考

一、创建API项目

项目结构截图
在这里插入图片描述
在这里插入图片描述

1、打开VS,选择创建新项目,选择ASP.NET Core Web API项目

2、采用多层架构,先创建四个文件夹(01应用程序 02业务逻辑 03数据访问 04基础设施

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b0e746c9eaa94db98497390b260e91bb.png

3、创建完成后,将API启动项放进应用程序内。将Service 和 Repository 分别放进业务逻辑和数据访问内即可。


二、引用ORM框架EFCore

1.在数据访问层放入一个类库DbEntities,在里面创建数据库的上下文DbContext

1、安装NuGet包,到当前类库
Microsoft,AspNetCore.ldentity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore
因为采用了微软的Identity,所以还安装了一下identity的包

2、将创建的上下文继承IdentityDbContext,并重写OnModelCreating

public class WPFAPIContext: IdentityDbContext
{
    /// <summary>
    /// 构造函数,接受参数为DbContextOptions。:base(options)初始化基类状态,遵循继承层次结构的初始化顺序
    /// </summary>
    /// <param name="connectionString"></param>
    public WPFAPIContext(DbContextOptions options):base(options) {
       
    }
    
    #region Sys
    public DbSet<UserInfo> UserInfo { get; set; }
    #endregion
    /// <summary>
    /// 重写OnModelCreating,配置模型关系
    /// </summary>
    /// <param name="modelBuilder"></param>
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserInfo>().HasQueryFilter(x=>!x.IsDeleted);
        base.OnModelCreating(modelBuilder);
    }
}

3、写好上下文类后,在Program.cs内注册数据库

builder.Services.AddDbContext<WPFAPIContext>(options => {
    //从配置文件中获取PostgreSQL数据库的连接字符串
    var connectionString = builder.Configuration.GetConnectionString("PostgresSql");
    // 使用Npgsql配置DbContextOptions。这里我使用的数据库是PGSql
    options.UseNpgsql(connectionString);
    //启用懒加载代理(对性能有影响)
    options.UseLazyLoadingProxies();
    // 启用敏感数据日志记录(在生产环境需谨慎使用)
    //options.EnableSensitiveDataLogging();
});

4、配置好以上后,采用EFCore的Model-First模式,创建数据库表。

1、打开程序包管理控制台。输入 create-migration youMigrationName (add-migration)创建一个数据库迁移,名字自定义
2、创建完成后,执行update-database后,即可创建成功数据库。
3、因为上下文继承的是IdentityContext,所以数据库里面会自动创建默认的权限表以及自己的自定义表。可以修改identity的表名字或者自己添加字段。
4、迁移完成后,数据库里面会多一张__EFMigrationsHistory表,表示每次的迁移记录,有Name和时间组成,可以利用这个进行回滚

三、引用AutoFac,对依赖进行管理

1、在基础设施层,创建一个Commons类库,在里面新建一个类AutoFacManage

2、再次添加包Autofac

1、添加好后,AutoFacManage继承Autofac.Module
2、重写autofac管道,实现通过反射批量注册注入

public class AutoFacManage:Autofac.Module
{
    /// <summary>
    /// 重写autofac管道,实现反射批量注册注入
    /// </summary>
    /// <param name="builder"></param>
    protected override void Load(ContainerBuilder builder)
    {
        #region 程序集注册依赖注入服务
        //获取的Repository和Service需要跟启动项挂钩,就是有引用关系,才能找到这个程序集
        var ApiRepository = Assembly.Load("Repository");
        //这里是约定名称以Repository结尾,进行一个依赖
        builder.RegisterAssemblyTypes(ApiRepository).Where(t => t.Name.EndsWith("Repository"))
           .AsImplementedInterfaces();

        var ApiService = Assembly.Load("Service");
        builder.RegisterAssemblyTypes(ApiService).Where(t=>t.Name.EndsWith("Service")).AsImplementedInterfaces();

        #endregion

        #region 单个依赖注入服务
        //名字不规范或者不在范围内,可以使用单个依赖注入
        //builder.RegisterType<ChatHub>().As<IChatHub>().SingleInstance();
        #endregion
        base.Load(builder);
    }
}

4、最后在Program里面调用这个方法

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
    .ConfigureContainer<ContainerBuilder>(builder =>
    {
        //调用 程序集 注册存入 方法
        builder.RegisterModule(new AutoFacManage());
    })

四、使用Serilog日志

1、在Commons里新建一个文件夹为Extensions,在里面创建一个SerilogLoggerWrapper类,用来封装保存日志的简单操作。
并且使用单例注入模式,进行一个全局的使用。这里需要安装Serilog的NuGet包。Serilog,Serilog.AspNetCore ,Serilog.Sinks.File 。
然后开始封装方法
 public class SerilogLoggerWrapper
 {
     private readonly LoggerConfiguration _loggerConfiguration;
     private string _defaultLogFileName;

     private SerilogLoggerWrapper()
     {
         _loggerConfiguration = new LoggerConfiguration();
         _defaultLogFileName = $"{DateTime.Now:yyyy-MM-dd}_{LogLevelEnum.Information}.log";
     }

     // 配置并获取Serilog日志记录器
     private ILogger BuildLogger()
     {
         //保存目录
         string logFolderPath = "Logs";
         string fullPath = Path.Combine(logFolderPath, _defaultLogFileName);

         _loggerConfiguration.WriteTo.File(new JsonFormatter(), fullPath)
        .WriteTo.Console();

         return _loggerConfiguration.CreateLogger();
     }

     // 保存日志的方法。这里采用的是枚举的形式对日志的等级进行保存
      //public enum LogLevelEnum
 //{
    // Verbose = 0,
    // Debug = 1,
    // Information = 2,
    // Warning = 3,
    // Error = 4,
    // Fatal = 5
 //}
     public void SaveLog(LogLevelEnum level, string logMessage)
     {
         ILogger logger = BuildLogger();

         switch (level)
         {
             case LogLevelEnum.Verbose:
                 logger.Verbose(logMessage);
                 break;
             case LogLevelEnum.Debug:
                 logger.Debug(logMessage);
                 break;
             case LogLevelEnum.Information:
                 logger.Information(logMessage);
                 break;
             case LogLevelEnum.Warning:
                 logger.Warning(logMessage);
                 break;
             case LogLevelEnum.Error:
                 logger.Error(logMessage);
                 break;
             case LogLevelEnum.Fatal:
                 logger.Fatal(logMessage);
                 break;
         }

         // 关闭并刷新日志记录器,确保日志被写入存储介质
         //logger.CloseAndFlush();

         // 这里不再通过ILogger实例调用CloseAndFlush,因为它没有这个方法

         // 使用Serilog.Log静态类来关闭和刷新日志
         Log.CloseAndFlush();
     }

     // 单例模式相关 - 静态实例字段
     private static SerilogLoggerWrapper _instance;

     // 单例模式相关 - 静态属性获取单例实例
     public static SerilogLoggerWrapper Instance
     {
         get
         {
             if (_instance == null)
             {
                 _instance = new SerilogLoggerWrapper();
             }
             return _instance;
         }
     }


 }

最后在AutoFacManage方法后面 向Autofac容器添加一个单例模式注入即可

 // 将SerilogLoggerWrapper以单例模式注入Autofac容器
 builder.Register(c => SerilogLoggerWrapper.Instance)
   .As<SerilogLoggerWrapper>()
   .SingleInstance();

五、使用Identity颁发令牌,并且存储在redis里面

1、引入redis

1、添加redis的连接"RedisConnectString": ":6379,password=123456"
2、在Commons里面新增类RedisInvokerExtension
public static class RedisInvokerExtension
{
    public static void AddRedisConfiguration(this IServiceCollection services, IConfiguration configuration)
    {
        // 读取配置文件中的Redis连接字符串
        var redisConnectionString = configuration.GetConnectionString("RedisConnectString");

        // 创建Redis连接对象
        var redis = ConnectionMultiplexer.Connect(redisConnectionString);

        // 将Redis连接对象注入到服务容器中,以便在其他地方可以使用
        services.AddSingleton<IConnectionMultiplexer>(redis);
    }
}
3、配置好后,在Program里引用
// 引用RedisInvokerExtension类进行Redis配置
builder.Services.AddRedisConfiguration(builder.Configuration);
4、再创建一个redis的操作类RedisOperator,配置一些基础操作
  public class RedisOperator
  {
      private readonly IConnectionMultiplexer _redis;

      public RedisOperator(IConnectionMultiplexer redis)
      {
          _redis = redis;
      }

      // 设置键值对
      public async Task<bool> SetValue(string key, string value)
      {
          var database = _redis.GetDatabase();
          return await database.StringSetAsync(key, value);
      }

      // 获取键值对
      public async Task<string> GetValue(string key)
      {
          var database = _redis.GetDatabase();
          return await database.StringGetAsync(key);
      }

      // 删除键
      public async Task<bool> DeleteKey(string key)
      {
          var database = _redis.GetDatabase();
          return await database.KeyDeleteAsync(key);
      }

      // 设置带有有效时间限制的键值对
      public async Task<bool> SetValueWithExpiration(string key, string value, TimeSpan expiration)
      {
          var database = _redis.GetDatabase();
          return await database.StringSetAsync(key, value, expiration);
      }

      // 检查键是否存在
      public async Task<bool> KeyExists(string key)
      {
          var database = _redis.GetDatabase();
          return await database.KeyExistsAsync(key);
      }
  }

2、配置仓储基类,实现一个简单的查询接口。

1、创建一个仓储接口基类 IRepositoryDAL(其余的方法可自定义,这里只做查询示范)
  /// <summary>
  /// 仓储接口基类
  /// </summary>
  /// <typeparam name="TEntity"></typeparam>
  public interface IRepositoryDAL<TEntity>where TEntity : class,new ()
  {
      /// <summary>
      /// 获取 当前实体的查询数据集(未跟踪到数据库)
      /// </summary>
      /// <value>
      /// The entities.
      /// </value>
      IQueryable<TEntity> Entities { get; }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="IsDeleted">是否查询被软删除的,默认false不查询</param>
      /// <returns></returns>
      IQueryable<TEntity> Queryable(bool IsDeleted = false);
  }

2、创建一个仓储基类实现接口RepositoryDAL。
//这里的IBaseEntity是实体的基类接口,方便查询筛选IsDeleted 条件
//使用的时候还要再定义一个BaseEntity类,使所有实体继承IBaseEntity,BaseEntity实现即可
  public class RepositoryDAL<TEntity> where TEntity : class, IBaseEntity, new()
  {
      private readonly WPFAPIContext _context;

      public RepositoryDAL(WPFAPIContext context) {
          _context = context;
      }

      /// <summary>
      /// 全查询
      /// </summary>
      public IQueryable<TEntity> Entities
      {
          get
          {
              return _context.Set<TEntity>().AsNoTracking();
          }
      }

      /// <summary>
      /// 查询
      /// </summary>
      /// <param name="IsDeleted"></param>
      /// <returns></returns>
      public IQueryable<TEntity> Queryable(bool IsDeleted = false){
          return _context.Set<TEntity>().AsNoTracking().Where(c => c.IsDeleted == IsDeleted);
      }
  }
3、仓储层即可这样写
//1、仓储接口
public interface IUserInfoRepository : IRepositoryDAL<UserInfo> { };

//2、仓储实现类

    public class UserInfoRepository : RepositoryDAL<UserInfo>, IUserInfoRepository
    { 
        public UserInfoRepository(WPFAPIContext context):base(context) {}
    }
//需要其他参数,自行添加即可
4、再定义Service层的接口和实现类,直接使用IRepositoryDAL的方法即可
  public class UserInfoService : IUserInfoService
  {
      private readonly IUserInfoRepository _userInfoRepository;
      public UserInfoService(IUserInfoRepository userInfoRepository) {
          _userInfoRepository = userInfoRepository;
      }

      /// <summary>
      /// 获取用户信息
      /// </summary>
      /// <returns></returns>
      /// <exception cref="NotImplementedException"></exception>
      public List<UserInfo> GetUserInfoList()
      {
        var result =   _userInfoRepository.Queryable(true).ToList();
          return result;
      }
  }
5、controller里面依赖注入service后测试接口成功获取到数据后,即完成

3、实现一个注册接口,要求密码为加密储存

1、新建一个String类型的扩展类,将字符串转为Md5(密钥可加可不加。md5很难被破译)
   /// <summary>
   /// 字符串扩展类
   /// </summary>
   public static class StringExtensions
   {
       public static string ToMD5(this string str)
       {
           //自定义密钥
           string secretKey = "111";
           // 将密钥和数据拼接
           string combinedData = secretKey + str;
           using (MD5 md5 = MD5.Create())
           {
               byte[] inputBytes = Encoding.UTF8.GetBytes(combinedData);
               byte[] hashBytes = md5.ComputeHash(inputBytes);

               StringBuilder sb = new StringBuilder();
               for (int i = 0; i < hashBytes.Length; i++)
               {
                   sb.Append(hashBytes[i].ToString("X2"));
               }

               return sb.ToString();
           }
       }
   }

2、引用FluentValidation,实现便捷的参数验证
//安装好FluentValidation的包后,新建文件,里面新建类,存放验证规则
namespace Commons.Validator.Sys
{
    public class SysUserInfoValidator
    {
    }

    /// <summary>
    /// 用户注册验证方法
    /// </summary>
    public class RegistUserParamesValidator : AbstractValidator<RegistUserParames>
    {
        public RegistUserParamesValidator()
        {
            RuleFor(x => x.UserName).NotEmpty().WithMessage("用户名不能为空");
            RuleFor(x => x.LoginAccont).NotEmpty().WithMessage("登录账号不能为空");
            RuleFor(x => x.VerifyCode).NotEmpty().WithMessage("验证码不能为空");
            RuleFor(x => x.PassWord).MinimumLength(4).WithMessage("密码最少为4位");
            RuleFor(x => x.AgainPassword).Equal(x => x.PassWord).WithMessage("两次输入的密码不一致");
            RuleFor(x => x.Email).EmailAddress().WithMessage("请输入有效的邮箱地址");
        }
    }

    /// <summary>
    /// 发送邮件验证码
    /// </summary>
    public class RegistEmailVerifyCodeParamesValidator : AbstractValidator<EmailVerifyCodeParames> {
        public RegistEmailVerifyCodeParamesValidator()
        {
            RuleFor(x => x.Email).EmailAddress().WithMessage("请输入有效的邮箱地址");
        }
    }
}

//在autofac容器注册
  #region 批量注入FluentValidation验证器
  var assembly = Assembly.GetExecutingAssembly();
  builder.RegisterAssemblyTypes(assembly)
        .Where(t => t.Name.EndsWith("Validator") && t.IsClass && !t.IsAbstract)
        .AsImplementedInterfaces()
        .SingleInstance();
  #endregion


//在controller层使用构造函数注入的方式使用即可
   private readonly ISysUserInfoService _sysUserInfoService;
  private readonly IValidator<RegistUserParames> _validator;
  private readonly IValidator<EmailVerifyCodeParames> _emailValidator;
  public UserController(ISysUserInfoService sysUserInfoService, IValidator<RegistUserParames> validator,
      IValidator<EmailVerifyCodeParames> emailValidator) {
      _sysUserInfoService = sysUserInfoService;
      _validator = validator;
      _emailValidator = emailValidator;
  }

 /// <summary>
 /// 用户注册
 /// </summary>
 /// <param name="parames"></param>
 /// <returns></returns>
 [HttpPost]
 [AllowAnonymous]
 public async Task<ApiResult<string>> RegistUser(RegistUserParames parames)
 {
     var validationResult = _validator.Validate(parames);
     //验证失败
     if (!validationResult.IsValid)
     {
         return await ErrorAsync(string.Join(",", validationResult.Errors.Select(e => e.ErrorMessage)));
     }
     var result = await _sysUserInfoService.RegistUser(parames);
     return await SuccessAsync(result);
 }
3、实现注册逻辑
  /// <summary>
  /// 用户注册
  /// </summary>
  /// <param name="parames"></param>
  /// <returns></returns>
  public async Task<string> RegistUser(RegistUserParames parames)
  {
      //验证邮箱与验证码是否一致.这里将发送的邮箱验证码存入了redis里面
      var code = await _redisOperator.GetValue(parames.Email);
      if (code != parames.VerifyCode) {
          return "验证码错误";
      }
      parames.PassWord = parames.PassWord.ToMD5();
      var insertUserInfo = _mapper.Map<Sys_UserInfo>(parames);
      insertUserInfo.AccountStatus = AccountStatusEnum.Inactive;
      insertUserInfo.RegistrationTime = DateTime.Now;
      insertUserInfo.Email = parames.Email;
      insertUserInfo.PhoneNumber = "182233";
      insertUserInfo.ProfilePicture = "11";
      _userInfoRepository.Insert(insertUserInfo);
      return "注册成功";
  }

4、实现登录接口,并颁发Token。(登录这一块还是打算手写一个JWT生成。权限不使用Identity,后面自己手动写权限这一块的表个逻辑)

1、创建一个JWTHelper类
public class JWTHelper
{
    private readonly IConfiguration _configuration;
    private readonly RedisOperator _redisOperator;


    public JWTHelper(IConfiguration configuration, RedisOperator redisOperator)
    {
        _configuration = configuration;
        _redisOperator = redisOperator;
    }
    /// <summary>
    /// 生成JWT
    /// </summary>
    /// <param name="tokenModel"></param>
    /// <returns></returns>
    public async Task<string> IssueJWT(TokenModel tokenModel)
    {
        var dateTime = DateTime.Now;
        var claims = new[]
    {
         new Claim(JwtRegisteredClaimNames.Jti,tokenModel.Uid.ToString()),//用户ID
         new Claim(ClaimTypes.Name,tokenModel.UserName),//用户Name
         new Claim("Project",tokenModel.Project),//项目
         new Claim(JwtRegisteredClaimNames.Iat, ((DateTimeOffset)dateTime).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
    };
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var expiration = int.Parse(_configuration["Jwt:ExpirationMinutes"]);

        var token = new JwtSecurityToken(
           issuer: _configuration["Jwt:Issuer"],
           audience: _configuration["Jwt:Audience"],
           claims: claims,
           expires: dateTime.AddDays(expiration),
           signingCredentials: creds
           );
        var Jwt = new JwtSecurityTokenHandler().WriteToken(token);

        var keyForRedis = $"jwt_{tokenModel.Uid}";
        var expirationTimeSpan = TimeSpan.FromDays(expiration);
        var isSet = await _redisOperator.SetValueWithExpiration(keyForRedis, Jwt, expirationTimeSpan);
        if (!isSet)
        {
            // 这里可以根据实际情况进行错误处理,比如抛出异常或者记录日志等
            throw new BusinessException("向redis保存Jwt失败");
        }
        return Jwt;
    }

    /// <summary>
    /// 验证JWT是否过期
    /// </summary>
    /// <param name="jwtStr"></param>
    /// <returns></returns>
    public async Task<bool> KeyExistsJwt(string jwtStr) {
        var jwtHandler = new JwtSecurityTokenHandler();
        JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
      
        var keyForRedis = $"jwt_{jwtToken.Id}";
      var result=  await _redisOperator.KeyExists(keyForRedis);
        return result;
    }

    /// <summary>
    /// 解析JWT
    /// </summary>
    /// <returns></returns>
    public TokenModel SerializeJWT(string jwtStr) {
        var jwtHandler = new JwtSecurityTokenHandler();
        var jwt = jwtStr.Substring("Bearer ".Length);
        JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwt);
        //解析自定义字段
        object project = new object();
        try
       {
           jwtToken.Payload.TryGetValue("Project", out project);
        }
        catch (Exception e)
        {
            Console.WriteLine("解析JWT报错:");
            Console.Write(e);
            throw;
        }
        var name = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;

        TokenModel tokenModel = new TokenModel()
        {
            Uid = long.Parse(jwtToken.Id),
            UserName = name,
            Project = project.ToString()
        };

        return tokenModel;
    }
}
2、登录的业务调用JWT生成即可
/// <summary>
/// 用户登录
/// </summary>
/// <param name="parames"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<string> Login(LoginParames parames)
{
  parames.PassWord = parames.PassWord.ToMD5();
  var userInfo = _userInfoRepository.Queryable().FirstOrDefault(c => c.LoginAccont == parames.LoginAccont && c.PassWord == parames.PassWord);
    if (userInfo != null) {
        TokenModel tokenModel = new TokenModel()
        {
            Uid = userInfo.Id,
            UserName = userInfo.UserName,
            Project = "DemoAPI"
        };
      var jwt = await  _jWTHelper.IssueJWT(tokenModel);
     return jwt;
    }
    return "未查询到数据";
}
3、对于邮件发送
//1、新建一个邮件的发送帮助类。在autofac注册后,即可使用
 public class EmailHelper
 {
     private readonly IConfiguration _configuration;

     public EmailHelper(IConfiguration configuration) { 
         _configuration = configuration;
     }
   
    /// <summary>
    /// 邮件发送
    /// </summary>
    /// <param name="parames"></param>
    /// <returns></returns>
     public bool SendEmail(SendEmailModel parames) {
         // 发件人邮箱地址,str
         var fromEmail = _configuration["Email:FromEmail"];
         // 发件人邮箱授权码(注意不是邮箱密码,需在QQ邮箱设置中开启SMTP服务后获取)
         string fromEmailPassword = _configuration["Email:Pwd"];
         // 收件人邮箱地址
         string toEmail = parames.ToEmail;
         // 邮件主题
         string subject = parames.Subject;
         // 邮件正文内容
         string body = parames.Body;
         try
         {
             // 创建SmtpClient实例,设置SMTP服务器地址和端口
             SmtpClient client = new SmtpClient("smtp.qq.com", 587);
             // 设置发件人邮箱的用户名和密码(这里的密码是授权码)
             client.Credentials = new NetworkCredential(fromEmail, fromEmailPassword);
             // 设置启用SSL加密
             client.EnableSsl = true;

             // 创建MailMessage实例,设置发件人、收件人、主题和正文等信息
             MailMessage message = new MailMessage(fromEmail, toEmail, subject, body);

             // 添加附件(暂时不加)
             //string attachmentPath = @"C:\YourAttachmentPath\example.txt"; // 替换为实际的附件路径
             //Attachment attachment = new Attachment(attachmentPath);
             //message.Attachments.Add(attachment);

             // 发送邮件
             client.Send(message);
             return true;

         }
         catch (Exception ex) {
             return false;
         }
     }

 }
 //autofac
 //InstancePerLifetimeScope 在一个特定的生命周期范围,服务只会被创建一次。占用较多内存 会释放
 //SingleInstance 对于整个程序的生命周期而言,只会被创建一次。占用内存稳定
 builder.RegisterType<EmailHelper>().As<EmailHelper>().InstancePerLifetimeScope();
//调用使用构造函数注入方式即可

5、验证颁发的token有效性。

1、新建一个JwtAuthorizationFilter自定义中间件。用来验证token的有效性
 public class JwtAuthorizationFilter
 {
     private readonly RedisOperator _redisOperator;
     private readonly JWTHelper _jWTHelper;

     public JwtAuthorizationFilter(RedisOperator redisOperator, JWTHelper jWTHelper) {
         _redisOperator = redisOperator;
         _jWTHelper = jWTHelper;
     }


     public async Task Invoke(HttpContext httpContext, RequestDelegate next) {

         // 获取当前请求对应的端点信息
         var endpoint = httpContext.GetEndpoint();

         // 检查端点是否有AllowAnonymous特性
         var allowAnonymous = endpoint?.Metadata?.OfType<AllowAnonymousAttribute>().Any();

         if (allowAnonymous == true)
         {
             // 如果有AllowAnonymous特性,直接跳过权限验证逻辑,将请求传递给下一个中间件或处理程序
             await next(httpContext);
             return;
         }

         var authorizationHeader = httpContext.Request.Headers["Authorization"].FirstOrDefault();
         //判断请求是否不为空带Bearer
         if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.StartsWith("Bearer "))
         {
             var jwt = authorizationHeader.Substring("Bearer ".Length);
             if ( !await _jWTHelper.KeyExistsJwt(jwt)) {
                 throw new BusinessException("权限验证失败,请检查!");
             }

         }
         else { 
             throw new BusinessException("权限验证失败,请检查!");
         }

         //验证通过,将信息放入httpContext
         var tokenModel = _jWTHelper.SerializeJWT(authorizationHeader);
         var claimList = new List<Claim>();
         //添加UserId申明
         var claimUserId = new Claim(ClaimTypes.NameIdentifier, tokenModel.Uid.ToString());
         claimList.Add(claimUserId);

         // 添加UserName声明
         var claimUserName = new Claim(ClaimTypes.Name, tokenModel.UserName);
         claimList.Add(claimUserName);

         // 添加UserAge声明,这里假设将年龄转换为字符串添加,你也可以根据需求采用其他合适的方式处理年龄信息
         //var claimUserAge = new Claim("UserAge", tokenModel.UserAge.ToString());
         //claimList.Add(claimUserAge);
         var identity = new ClaimsIdentity(claimList);
         var principal = new ClaimsPrincipal(identity);
         await next(httpContext);
     }
 }
2、在program里面加入
// 添加自定义中间件JwtAuthorizationFilter
var jwtAuthorizationFilter = app.Services.GetRequiredService<JwtAuthorizationFilter>();
app.Use(async (context, next) =>
{
    await jwtAuthorizationFilter.Invoke(context, next);
});

//添加好后,默认对请求进行权限验证。要对接口不进行权限验证,接口加上 [AllowAnonymous] 特性即可

总结

到此完毕结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值