添加授权鉴权模块
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
using JWT;
|
||||
using JWT.Algorithms;
|
||||
using JWT.Builder;
|
||||
using JWT.Exceptions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Auth.JwtBearer.Authentication.Options;
|
||||
using Yi.Framework.Core.Helper;
|
||||
|
||||
namespace Yi.Framework.Auth.JwtBearer.Authentication
|
||||
{
|
||||
public class JwtTokenManager
|
||||
{
|
||||
private JwtTokenOptions _jwtTokenOptions;
|
||||
|
||||
public JwtTokenManager(IOptions<JwtTokenOptions> options)
|
||||
{
|
||||
_jwtTokenOptions = options.Value;
|
||||
}
|
||||
public string CreateToken(Dictionary<string, object>? claimDic)
|
||||
{
|
||||
var token = JwtBuilder.Create()
|
||||
.WithAlgorithm(new RS256Algorithm(RSAFileHelper.GetKey(), RSAFileHelper.GetKey()))
|
||||
.AddClaim(ClaimName.Issuer, _jwtTokenOptions.Issuer)
|
||||
.AddClaim(ClaimName.Audience, _jwtTokenOptions.Audience)
|
||||
.AddClaim(ClaimName.Subject, _jwtTokenOptions.Subject)
|
||||
.AddClaim(ClaimName.IssuedAt, UnixEpoch.GetSecondsSince(new DateTimeOffset(DateTime.UtcNow)))
|
||||
.ExpirationTime(DateTime.Now.AddSeconds(_jwtTokenOptions.ExpSecond));
|
||||
if (claimDic is not null)
|
||||
{
|
||||
foreach (var d in claimDic)
|
||||
{
|
||||
token.AddClaim(d.Key, d.Value);
|
||||
};
|
||||
}
|
||||
return token.Encode();
|
||||
}
|
||||
|
||||
public IDictionary<string, object>? VerifyToken(string token, TokenVerifyErrorAction tokenVerifyErrorAction)
|
||||
{
|
||||
IDictionary<string, object>? claimDic = null;
|
||||
try
|
||||
{
|
||||
|
||||
claimDic = JwtBuilder.Create()
|
||||
.WithAlgorithm(new RS256Algorithm(RSAFileHelper.GetPublicKey()))
|
||||
.WithValidationParameters(ValidationParameters.Default)
|
||||
.Decode<IDictionary<string, object>>(token);
|
||||
}
|
||||
catch (TokenNotYetValidException ex)
|
||||
{
|
||||
if (tokenVerifyErrorAction.TokenNotYetValidAction is not null)
|
||||
{
|
||||
tokenVerifyErrorAction.TokenNotYetValidAction(ex);
|
||||
}
|
||||
//Console.WriteLine("Token错误");
|
||||
}
|
||||
catch (TokenExpiredException ex)
|
||||
{
|
||||
if (tokenVerifyErrorAction.TokenExpiredAction is not null)
|
||||
{
|
||||
tokenVerifyErrorAction.TokenExpiredAction(ex);
|
||||
}
|
||||
//Console.WriteLine("Token过期");
|
||||
}
|
||||
catch (SignatureVerificationException ex)
|
||||
{
|
||||
if (tokenVerifyErrorAction.SignatureVerificationAction is not null)
|
||||
{
|
||||
tokenVerifyErrorAction.SignatureVerificationAction(ex);
|
||||
}
|
||||
//Console.WriteLine("Token无效");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (tokenVerifyErrorAction.ErrorAction is not null)
|
||||
{
|
||||
tokenVerifyErrorAction.ErrorAction(ex);
|
||||
}
|
||||
//Console.WriteLine("Token内部错误,json序列化");
|
||||
}
|
||||
return claimDic;
|
||||
|
||||
}
|
||||
|
||||
public class TokenVerifyErrorAction
|
||||
{
|
||||
public Action<TokenNotYetValidException>? TokenNotYetValidAction { get; set; }
|
||||
|
||||
public Action<TokenExpiredException>? TokenExpiredAction { get; set; }
|
||||
public Action<SignatureVerificationException>? SignatureVerificationAction { get; set; }
|
||||
|
||||
public Action<Exception>? ErrorAction { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Auth.JwtBearer.Authentication.Options
|
||||
{
|
||||
public class JwtTokenOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 听众
|
||||
/// </summary>
|
||||
public string Audience { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 发行者
|
||||
/// </summary>
|
||||
public string Issuer { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 主题
|
||||
/// </summary>
|
||||
public string Subject { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间,单位秒
|
||||
/// </summary>
|
||||
public long ExpSecond { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using Yi.Framework.Core.Helper;
|
||||
|
||||
namespace Yi.Framework.Auth.JwtBearer.Authentication
|
||||
{
|
||||
public class YiJwtAuthenticationHandler : IAuthenticationHandler
|
||||
{
|
||||
private JwtTokenManager _jwtTokenManager;
|
||||
public YiJwtAuthenticationHandler(JwtTokenManager jwtTokenManager)
|
||||
{
|
||||
_jwtTokenManager = jwtTokenManager;
|
||||
}
|
||||
public const string YiJwtSchemeName = "YiJwtAuth";
|
||||
|
||||
private AuthenticationScheme _scheme;
|
||||
private HttpContext _context;
|
||||
/// <summary>
|
||||
/// 初始化数据
|
||||
/// </summary>
|
||||
/// <param name="scheme"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
|
||||
{
|
||||
_scheme = scheme;
|
||||
_context = context;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成认证票据
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="role"></param>
|
||||
/// <returns></returns>
|
||||
private AuthenticationTicket GetAuthTicket(IDictionary<string, object> dicClaims)
|
||||
{
|
||||
List<Claim> claims = new List<Claim>();
|
||||
foreach (var claim in dicClaims)
|
||||
{
|
||||
var p = (JsonElement)claim.Value;
|
||||
string? resp=null;
|
||||
switch (p.ValueKind)
|
||||
{
|
||||
|
||||
case JsonValueKind.String:
|
||||
resp = p.GetString();
|
||||
break;
|
||||
case JsonValueKind.Number:
|
||||
resp = p.GetInt32().ToString();
|
||||
break;
|
||||
}
|
||||
claims.Add(new Claim(claim.Key, resp ?? ""));
|
||||
}
|
||||
var claimsIdentity = new ClaimsIdentity(claims.ToArray(), YiJwtSchemeName);
|
||||
var principal = new ClaimsPrincipal(claimsIdentity);
|
||||
return new AuthenticationTicket(principal, _scheme.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理操作
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public Task<AuthenticateResult> AuthenticateAsync()
|
||||
{
|
||||
AuthenticateResult result = AuthenticateResult.Fail("未发现授权令牌");
|
||||
_context.Request.Headers.TryGetValue("Authorization", out StringValues values);
|
||||
string valStr = values.ToString();
|
||||
if (!string.IsNullOrWhiteSpace(valStr))
|
||||
{
|
||||
var tokenHeader = valStr.Substring(0, 6);
|
||||
if (tokenHeader == "Bearer")
|
||||
{
|
||||
var token = valStr.Substring(7);
|
||||
|
||||
var claimDic = _jwtTokenManager.VerifyToken(token, new JwtTokenManager.TokenVerifyErrorAction()
|
||||
{
|
||||
TokenExpiredAction = (ex) => { result = AuthenticateResult.Fail("Token过期"); },
|
||||
SignatureVerificationAction = (ex) => { result = AuthenticateResult.Fail("Token效验失效"); },
|
||||
TokenNotYetValidAction = (ex) => { result = AuthenticateResult.Fail("Token完全错误"); },
|
||||
ErrorAction = (ex) => { result = AuthenticateResult.Fail("Token内部错误"); }
|
||||
});
|
||||
if (claimDic is not null)
|
||||
{
|
||||
//成功
|
||||
result = AuthenticateResult.Success(GetAuthTicket(claimDic));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = AuthenticateResult.Fail("授权令牌格式错误");
|
||||
}
|
||||
|
||||
}
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 未登录时的处理
|
||||
/// </summary>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public Task ChallengeAsync(AuthenticationProperties? properties)
|
||||
{
|
||||
_context.Request.Headers.TryGetValue("Authorization", out StringValues values);
|
||||
_context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 权限不足的处理
|
||||
/// </summary>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public Task ForbidAsync(AuthenticationProperties? properties)
|
||||
{
|
||||
_context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Core.CurrentUsers;
|
||||
|
||||
namespace Yi.Framework.Auth.JwtBearer.Authorization
|
||||
{
|
||||
public class DefaultPermissionHandler : IPermissionHandler
|
||||
{
|
||||
public bool IsPass(string permission, ICurrentUser currentUser)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Core.CurrentUsers;
|
||||
|
||||
namespace Yi.Framework.Auth.JwtBearer.Authorization
|
||||
{
|
||||
public interface IPermissionHandler
|
||||
{
|
||||
bool IsPass(string permission,ICurrentUser currentUser);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Core.CurrentUsers;
|
||||
using Yi.Framework.Core.Exceptions;
|
||||
using Yi.Framework.Core.Model;
|
||||
|
||||
namespace Yi.Framework.Auth.JwtBearer.Authorization
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class PermissionAttribute : ActionFilterAttribute
|
||||
{
|
||||
private string Permission { get; set; }
|
||||
|
||||
public PermissionAttribute(string permission)
|
||||
{
|
||||
this.Permission = permission;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动作鉴权
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
|
||||
var permissionHandler = ServiceLocatorModel.Instance.GetRequiredService<IPermissionHandler>();
|
||||
|
||||
var currentUser = ServiceLocatorModel.Instance.GetRequiredService<ICurrentUser>();
|
||||
|
||||
|
||||
|
||||
var result = permissionHandler.IsPass(Permission, currentUser);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
throw new AuthException(message: "您无权限访问该接口");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JWT" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yi.Framework.AspNetCore\Yi.Framework.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StartupModules;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Auth.JwtBearer.Authentication;
|
||||
using Yi.Framework.Auth.JwtBearer.Authentication.Options;
|
||||
using Yi.Framework.Auth.JwtBearer.Authorization;
|
||||
using Yi.Framework.Core;
|
||||
using Yi.Framework.Core.Attributes;
|
||||
using Yi.Framework.Core.Configuration;
|
||||
|
||||
namespace Yi.Framework.Auth.JwtBearer
|
||||
{
|
||||
[DependsOn(typeof(YiFrameworkCoreModule))]
|
||||
public class YiFrameworkAuthJwtBearerModule : IStartupModule
|
||||
{
|
||||
public void Configure(IApplicationBuilder app, ConfigureMiddlewareContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services, ConfigureServicesContext context)
|
||||
{
|
||||
services.Configure<JwtTokenOptions>(Appsettings.appConfiguration("JwtTokenOptions"));
|
||||
services.AddAuthentication(YiJwtAuthenticationHandler.YiJwtSchemeName);
|
||||
services.AddTransient<JwtTokenManager>();
|
||||
services.AddSingleton<IPermissionHandler, DefaultPermissionHandler>();
|
||||
services.AddAuthentication(option =>
|
||||
{
|
||||
option.AddScheme<YiJwtAuthenticationHandler>(YiJwtAuthenticationHandler.YiJwtSchemeName, YiJwtAuthenticationHandler.YiJwtSchemeName);
|
||||
});
|
||||
services.AddSingleton<PermissionAttribute>(_=>new PermissionAttribute(string.Empty));
|
||||
services.AddControllers(options => {
|
||||
options.Filters.Add<PermissionAttribute>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user