.net6 JWT权限验证

首先写一个JWT token生成方法

cs 复制代码
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WebApiBasics.Helper.JwtHelper
{
    public interface IJwtService
    {
        /// <summary>
        /// 生成JWT
        /// </summary>
        /// <returns></returns>
        string GenerateAccessToken(string InputJson, DateTime tokenDate, int minutes = 30);

        /// <summary>
        /// 刷新Token
        /// </summary>
        /// <returns></returns>
        string GenerateRefreshToken();

        /// <summary>
        /// 刷新Token
        /// </summary>
        /// <returns></returns>
        string GenerateJwtToken(string oldToken);

        /// <summary>
        /// 验证JWT
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        bool ValidateAccessToken(string token);
    }
}

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Linq;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using WebApiBasics.Entity.Dto.CommonDto;
using WebApiBasics.Entity.Dto.QueryDto;
using WebApiBasics.Helper.ConverHelper;

namespace WebApiBasics.Helper.JwtHelper
{
    public class JwtService : IJwtService
    {
        private readonly IConfiguration _configuration;

        public JwtService(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        /// <summary>
        /// 生成JWT
        /// </summary>
        /// <returns></returns>
        public string GenerateAccessToken(string InputJson, DateTime tokenDate, int minutes = 30)
        {
            var signingAlgorithm = SecurityAlgorithms.HmacSha256;
            var claims = new[]
            {
            new Claim(ClaimTypes.Role,"Admin"),
            new Claim("userinfo",InputJson),
             };

            var secretByte = Encoding.UTF8.GetBytes(JwtSettings.IssuerSigningKey);
            var signingKey = new SymmetricSecurityKey(secretByte);
            var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);

            var token = new JwtSecurityToken(
                issuer: JwtSettings.ValidIssuer,//_configuration["JWT:ValidIssuer"],
                audience: JwtSettings.ValidAudience,//_configuration["JWT:ValidAudience"],
                claims,
                notBefore: tokenDate,
                expires: tokenDate.AddMinutes(minutes),
                signingCredentials
            );

            var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

            return "Bearer " + tokenStr;
        }

        /// <summary>
        /// 刷新Token
        /// </summary>
        /// <returns></returns>
        public string GenerateRefreshToken()
        {
            var randomNumber = new byte[32];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(randomNumber);
                return Convert.ToBase64String(randomNumber);
            }
        }

        /// <summary>
        /// 刷新Token
        /// </summary>
        /// <returns></returns>
        public string GenerateJwtToken(string oldToken)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(JwtSettings.IssuerSigningKey);
            try
            {
                tokenHandler.ValidateToken(oldToken, new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = JwtSettings.ValidIssuer,//_configuration["JWT:ValidIssuer"],
                    ValidAudience = JwtSettings.ValidAudience,//_configuration["JWT:ValidAudience"],
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ClockSkew = TimeSpan.FromMinutes(Convert.ToInt32(JwtSettings.ClockSkew)),
                    ValidAudiences = new string[] { JwtSettings.ValidAudience }
                }, out var validatedToken);
                //token信息
                var jwtToken = (JwtSecurityToken)validatedToken;
                //过期时间
                var expires = jwtToken.Payload.Exp;
                //当前时间 中国地区加8小时
                var now = DateTime.UtcNow.AddHours(8);
                //换算过期时间
                System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
                dtDateTime = dtDateTime.AddSeconds((double)expires).AddHours(8);
                //在有效期的50%时候刷新token
                if (dtDateTime > now && (dtDateTime - now).TotalMinutes < 15)
                {
                    return GenerateJwtToken(jwtToken);
                }
                else
                {
                    //不到15分钟返回传进来的token
                    return "Bearer " + oldToken;
                }
            }
            catch (Exception)
            {
                return "Bearer " + oldToken;
            }
        }

        /// <summary>
        /// 验证JWT
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public bool ValidateAccessToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(JwtSettings.IssuerSigningKey);
            try
            {
                tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = JwtSettings.ValidIssuer,//_configuration["JWT:ValidIssuer"],
                    ValidAudience = JwtSettings.ValidAudience,//_configuration["JWT:ValidAudience"],
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ClockSkew = TimeSpan.FromMinutes(Convert.ToInt32(JwtSettings.ClockSkew)),
                    ValidAudiences = new string[] { JwtSettings.ValidAudience }
                }, out var validatedToken);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 刷新Token
        /// </summary>
        /// <returns></returns>
        private string GenerateJwtToken(JwtSecurityToken oldToken)
        {
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettings.IssuerSigningKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var newToken = new JwtSecurityToken(
                issuer: oldToken.Issuer,
                audience: oldToken.Audiences.FirstOrDefault(),
                claims: oldToken.Claims,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: creds
            );

            var tokenStr = new JwtSecurityTokenHandler().WriteToken(newToken);
            return "Bearer " + tokenStr;
        }
    }
}

在program.cs中注册接口类

cs 复制代码
//接口类需要 依赖注入
bill.Services.AddScoped<IJwtService, JwtService>();

写一个自定义权限验证类

cs 复制代码
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebApiBasics.DataBase.SqlServer;
using WebApiBasics.Entity.SystemTable;
using Microsoft.Extensions.DependencyInjection;
using WebApiBasics.Helper.ConverHelper;
using WebApiBasics.Entity.Dto.OutDto;
using WebApiBasics.Helper.JwtHelper;
using WebApiBasics.Entity.Dto.ResultDto;
using WebApiBasics.Helper.AppSettingHelper;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using WebApiBasics.Helper.UserHelper;

namespace WebApiBasics.Helper.CustomAuthHelper
{
    /// <summary>
    /// 自定义权限验证
    /// </summary>
    public class CustomActionFilter : ActionFilterAttribute
    {
        private IJwtService _jwtService;
        private SysUserManager _userManager;

        /// <summary>
        /// 进入控制器或方法前
        /// </summary>
        /// <param name="context"></param>
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            //获取token信息
            var token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
            //注入中间件将从根生存期解析它,因为它没有访问范围的权限。请从上下文提供者解析它,因为它可以访问范围
            _jwtService = context.HttpContext.RequestServices.GetRequiredService<IJwtService>();
            _userManager = context.HttpContext.RequestServices.GetRequiredService<SysUserManager>();
            if (_jwtService.ValidateAccessToken(token))
            {
                //刷新token
                string newToken = _jwtService.GenerateJwtToken(token);
                //这一步必须有,否则前端获取不到token
                context.HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "refresh-Token");
                //存入到头部,方便前端获取到token
                context.HttpContext.Response.Headers.Add("refresh-Token", newToken);
                context.HttpContext.Response.Headers["Authorization"] = newToken;

                //记录token信息
                if ("Bearer " + token != newToken)
                {
                    //注入中间件将从根生存期解析它,因为它没有访问范围的权限。请从上下文提供者解析它,因为它可以访问范围
                    SqlServerDbContext _context = context.HttpContext.RequestServices.GetRequiredService<SqlServerDbContext>();
                    SystemUserToken systemUserToken = new SystemUserToken();
                    systemUserToken.UserId = _userManager.systemUser.Id;
                    systemUserToken.Token = newToken;
                    systemUserToken.CreatTime = DateTime.Now;
                    systemUserToken.ExpirationTime = 30;
                    _context.SystemUserToken.Add(systemUserToken);
                    _context.SaveChanges();
                }
            }
            else
            {
                //返回身份验证失败
                Results<LoginOutDto> results = Results<LoginOutDto>.GetResult(400, "身份验证失败!", new LoginOutDto(){ Token = token}, false);
                string responseString = results.ToJson();
                //修改日志
                UpdateLog(context.HttpContext, responseString);

                context.HttpContext.Response.StatusCode = 400;
                context.Result = new UnauthorizedResult();
                byte[] data = Encoding.UTF8.GetBytes(responseString);
                var bodyControlFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
                if (bodyControlFeature != null)
                {
                    bodyControlFeature.AllowSynchronousIO = true;
                }
                context.HttpContext.Response.Body.Write(data, 0, data.Length);
            }
        }

        /// <summary>
        /// OnActionExecuted方法在Controller的Action执行后执行
        /// </summary>
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            //TODO
            IActionResult? result = (Microsoft.AspNetCore.Mvc.ObjectResult)context.Result;
            if (result != null && ((Microsoft.AspNetCore.Mvc.ObjectResult)result).Value != null)
            {
                string responseString = ((Microsoft.AspNetCore.Mvc.ObjectResult)result).Value.ToJson();
                UpdateLog(context.HttpContext, responseString);
            }
        }

        /// <summary>
        /// 修改日志返回结果
        /// </summary>
        /// <param name="context"></param>
        /// <param name="json"></param>
        private void UpdateLog(HttpContext context,string json)
        {
            if (context.Items["logId"] != null)
            {
                int logid = Convert.ToInt32(context.Items["logId"]);
                //注入中间件将从根生存期解析它,因为它没有访问范围的权限。请从上下文提供者解析它,因为它可以访问范围
                SqlServerDbContext _context = context.RequestServices.GetRequiredService<SqlServerDbContext>();
                SystemLogs systemLogs = _context.SystemLogs.Find(logid);
                if (systemLogs != null)
                {
                    systemLogs.ResponseString = json;
                    _context.SaveChanges();
                }
            }
        }
    }
}

在控制器中使用

cs 复制代码
 [Route("api/[controller]")]
 [ApiController]
 [CustomActionFilter]
 public class AuthenticateController : ControllerBase

如需所有接口日志,可以写自定义认证中间件

cs 复制代码
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebApiBasics.DataBase.SqlServer;
using WebApiBasics.Entity.Dto.OutDto;
using WebApiBasics.Entity.SystemTable;
using Microsoft.Extensions.DependencyInjection;
using WebApiBasics.Helper.ConverHelper;
using WebApiBasics.Helper.AppSettingHelper;
using WebApiBasics.Entity.Dto.ResultDto;
using WebApiBasics.Helper.JwtHelper;

namespace WebApiBasics.Helper.CustomAuthHelper
{
    /// <summary>
    /// 自定义认证中间件
    /// </summary>
    public class CustomAuthenticationMiddleware
    {
        private readonly RequestDelegate _next;
        private SqlServerDbContext _context;
        private IJwtService _jwtService;

        public CustomAuthenticationMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            //注入中间件将从根生存期解析它,因为它没有访问范围的权限。请从上下文提供者解析它,因为它可以访问范围
            _context = context.RequestServices.GetRequiredService<SqlServerDbContext>();
            _jwtService = context.RequestServices. GetRequiredService<IJwtService>();
            //请求地址
            string requestUrl = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}";
            string host = context.Request.Host.Value;
            string path = context.Request.Path;
            string scheme = context.Request.Scheme;
            string url = scheme + "://" + host + path;
            //请求类型
            string method = context.Request.Method;
            //请求方法
            string action = "";
            //请求模块
            string controller = "";
            //请求IP
            string ip = context.Connection.RemoteIpAddress?.ToString();
            if (ip == "::1")
                ip = "127.0.0.1";

            RouteValueDictionary route = context.Request.RouteValues;
            if (route.Count > 0)
            {
                if (route.ContainsKey("action"))
                {
                    action = route["action"] == null ? "" : route["action"].ToString();
                }
                if (route.ContainsKey("controller"))
                {
                    controller = route["controller"] == null ? "" : route["controller"].ToString();
                }
            }

            // 确保请求的body可以被多次读取
            context.Request.EnableBuffering();
            // 读取请求的body
            var requestBody = await new StreamReader(context.Request.Body, Encoding.UTF8).ReadToEndAsync();

            // 重置Request.Body的位置,以确保后续的中间件可以再次读取
            context.Request.Body.Position = 0;

            //添加日志
            SystemLogs systemLogs = new SystemLogs();
            systemLogs.Date = DateTime.Now;
            systemLogs.RequsetId = 0;
            systemLogs.Url = requestUrl;
            systemLogs.RequsetString = requestBody.Replace(" ", "").Replace("\n", "");
            systemLogs.ResponseString = "";
            systemLogs.Method = method;
            systemLogs.Action = action;
            systemLogs.Controller = controller;
            systemLogs.Ip = ip;
            _context.SystemLogs.Add(systemLogs);
            await _context.SaveChangesAsync();
            var logId = systemLogs.Id;
            //保存日志ID 后续返回值需要修改
            context.Items["logId"] = logId;
            await _next(context);
        }
    }
}

自定义认证中间件也需要在program.cs中注册

cs 复制代码
//自定义认证中间件
app.UseMiddleware<CustomAuthenticationMiddleware>();

通过token获取当前用户

cs 复制代码
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebApiBasics.DataBase.SqlServer;
using WebApiBasics.Entity.Dto.CommonDto;
using WebApiBasics.Entity.Dto.Enum;
using WebApiBasics.Entity.SystemTable;

namespace WebApiBasics.Helper.UserHelper
{
    /// <summary>
    /// 当前登录用户
    /// </summary>
    public class SysUserManager
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly SqlServerDbContext _context;
        private readonly QuerySqlData _querySqlData;

        /// <summary>
        /// 用户信息
        /// </summary>
        public SystemUser systemUser
        {
            get => GetSystemUser();
        }

        /// <summary>
        /// 是否是管理员
        /// </summary>
        public bool IsAdmin
        {
            get => JudgmentAdmin();
        }

        public SysUserManager(IHttpContextAccessor httpContextAccessor, SqlServerDbContext context)
        {
            _httpContextAccessor = httpContextAccessor;
            _context = context;
            _querySqlData = new QuerySqlData(context);
        }

        /// <summary>
        /// 获取Claims中的值
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string GetClaims(string name)
        {
            try
            {
                string result = string.Empty;
                var token = _httpContextAccessor.HttpContext.Request.Headers["Authorization"].ToString().Split(" ").Last();
                var handler = new JwtSecurityTokenHandler();
                var jwtToken = handler.ReadJwtToken(token);

                foreach (var claim in jwtToken.Claims)
                {
                    // 你可以在这里处理你的Claims
                    // 例如,获取特定的Claim
                    if (claim.Type == name)
                    {
                        result = claim.Value;
                        break;
                    }
                }
                return result;
            }
            catch (Exception)
            {
                return "";
            }
        }

        /// <summary>
        /// 获取SystemUser
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public SystemUser GetSystemUser()
        {
            try
            {
                SystemUser systemUser = new SystemUser();
                systemUser = JsonConvert.DeserializeObject<SystemUser>(GetClaims("userinfo"));
                //可能会更新用户信息 所以根据ID查一次
                if (systemUser != null)
                {
                    systemUser = _context.SystemUser.FirstOrDefault(u => u.Id == systemUser.Id);
                }
                return systemUser ?? new SystemUser();
            }
            catch (Exception)
            {
                return new SystemUser();
            }
        }

        /// <summary>
        /// 是否是管理员
        /// </summary>
        /// <returns></returns>
        public bool JudgmentAdmin()
        {
            bool result = false;
            //下面写自己的验证方法
            SystemUser systemUser = GetSystemUser();
            if (systemUser.Id > 0)
            {
                List<SystemRole> roleList = _querySqlData.QueryRoleList(systemUser.Id);
                SystemRole systemRole = roleList.FirstOrDefault(u => u.RoleCode.ToLower() == "admin");
                if (systemRole != null)
                {
                    result = true;
                }
            }
            return result;
        }
    }
}

同样需要注册 因为要获取上下文

cs 复制代码
 // 注册服务
 bill.Services.AddTransient<SysUserManager>();

控制器中可以直接只用

cs 复制代码
namespace WebApiBasics.Core.Service
{
    /// <summary>
    /// 用户模块
    /// </summary>
    [Route("api/SystemUser/[action]")]
    [ApiController]
    [CustomActionFilter]
    public class SystemUserService : ControllerBase
    {
        private readonly SysUserManager _sysUserManager;
        private readonly SqlServerDbContext _context;
        private readonly ILogger<SystemUserService> _logger;

        /// <summary>
        /// 用户模块
        /// </summary>
        public SystemUserService(SqlServerDbContext context, ILogger<SystemUserService> logger, SysUserManager sysUserManager)
        {
            _context = context;
            _logger = logger;
            _sysUserManager = sysUserManager;
        }

        /// <summary>
        /// 查询用户信息
        /// </summary>
        /// <param name="Input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<Results<UserInfoOutDto>> QuerySystemUserInfo()
        {
            //输出用户信息
            UserInfoOutDto userInfoOutDto = new UserInfoOutDto();
            userInfoOutDto.UserName = _sysUserManager.systemUser.UserName;
            return Results<UserInfoOutDto>.DataResult(userInfoOutDto);
        }
    }
}
相关推荐
坐井观老天3 小时前
在C#中使用资源保存图像和文本和其他数据并在运行时加载
开发语言·c#
pchmi6 小时前
C# OpenCV机器视觉:模板匹配
opencv·c#·机器视觉
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭7 小时前
C#都可以找哪些工作?
开发语言·c#
1900438 小时前
.NET重点
.net
m0_663234018 小时前
在 .NET 5.0 运行 .NET 8.0 教程:使用 ASP.NET Core 创建 Web API
前端·asp.net·.net
boligongzhu9 小时前
Dalsa线阵CCD相机使用开发手册
c#
向宇it21 小时前
【从零开始入门unity游戏开发之——C#篇23】C#面向对象继承——`as`类型转化和`is`类型检查、向上转型和向下转型、里氏替换原则(LSP)
java·开发语言·unity·c#·游戏引擎·里氏替换原则
sukalot1 天前
windows C#-命名实参和可选实参(下)
windows·c#
小码编匠1 天前
.NET 下 RabbitMQ 队列、死信队列、延时队列及小应用
后端·c#·.net