首先写一个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);
}
}
}