added jwt bearer configuration

master
Young 7 months ago
parent cec26038f5
commit 41bc72d53b

@ -0,0 +1,73 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Infrastructure.Options;
using Infrastructure.Security;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace Infrastructure.Extensions;
public static class AuthenticationSetup
{
/// <summary>
/// 配置认证服务包含jwt认证
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="builderOptions">自定义认证服务配置</param>
/// <returns></returns>
public static IServiceCollection AddDefaultAuthentication(
this IServiceCollection services,
IConfiguration configuration,
Action<AuthenticationBuilder>? builderOptions = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
var audienceOptions = configuration.GetSection(AudienceOptions.Name).Get<AudienceOptions>();
if (audienceOptions is null || !audienceOptions.IsEnable)
{
return services;
}
services.AddSingleton<JwtSecurityTokenHandler>();
services.AddScoped<IUserContext, UserContext>();
services.AddSingleton<DefaultTokenHandler>();
services.AddSingleton<IEncryptionService, EncryptionService>();
services.AddSingleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerOptionsPostConfigureOptions>();
services.AddSingleton<ITokenBuilder, TokenBuilder>();
var key = configuration["AUDIENCE_KEY"] ?? audienceOptions.Secret;
ArgumentException.ThrowIfNullOrEmpty(key);
var buffer = Encoding.UTF8.GetBytes(key);
var securityKey = new SymmetricSecurityKey(buffer);
var tokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = securityKey,
ValidIssuer = audienceOptions.Issuer,
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = audienceOptions.Audience,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(300),
RequireExpirationTime = true,
RoleClaimType = ClaimTypes.Role
};
var builder = services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = nameof(DefaultAuthenticationHandler);
options.DefaultForbidScheme = nameof(DefaultAuthenticationHandler);
});
builder.AddScheme<AuthenticationSchemeOptions, DefaultAuthenticationHandler>(
nameof(DefaultAuthenticationHandler),
options => { });
builder.AddJwtBearer(options => { options.TokenValidationParameters = tokenValidationParameters; });
builderOptions?.Invoke(builder);
return services;
}
}

@ -2,8 +2,6 @@ using System.Text.Encodings.Web;
using Infrastructure.Utils;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Infrastructure.Security;
@ -14,6 +12,7 @@ public class DefaultAuthenticationHandler(
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
private const string Message = "You are not authorized to access this resource";
private const string ContentType = "application/json";
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
@ -22,7 +21,7 @@ public class DefaultAuthenticationHandler(
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
Response.ContentType = ContentType;
Response.StatusCode = StatusCodes.Status200OK;
await Response.WriteAsync(new MessageData(false, Message, 403)
.Serialize());
@ -30,9 +29,9 @@ public class DefaultAuthenticationHandler(
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
Response.ContentType = ContentType;
Response.StatusCode = StatusCodes.Status200OK;
await Response.WriteAsync(new MessageData(false,Message, 401)
await Response.WriteAsync(new MessageData(false, Message, 401)
.Serialize());
}
}

@ -1,5 +1,3 @@
using System.Text;
using Microsoft.Extensions.Configuration;
using System.Security.Cryptography;
namespace Infrastructure.Security;

@ -0,0 +1,45 @@
using System.Security.Claims;
using Infrastructure.Utils;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.JsonWebTokens;
namespace Infrastructure.Security;
public interface ITokenBuilder
{
IList<Claim> GetClaimsByUserContext(IUserContext userContext);
void SetUserContext(TokenValidatedContext context);
}
public class TokenBuilder : ITokenBuilder
{
public IList<Claim> GetClaimsByUserContext(IUserContext userContext)
{
var claims = new List<Claim>()
{
new(JwtRegisteredClaimNames.UniqueName, userContext.Username),
new(JwtRegisteredClaimNames.NameId, userContext.Id.ToString()),
new(JwtRegisteredClaimNames.Name, userContext.Name),
new(JwtRegisteredClaimNames.Email, userContext.Email),
};
claims.AddRange(userContext.RoleIds.Select(rId => new Claim(ClaimTypes.Role, rId)));
return claims;
}
public void SetUserContext(TokenValidatedContext context)
{
var userContext = context.HttpContext.RequestServices.GetService<IUserContext>() ??
throw new NullReferenceException(nameof(IUserContext));
var principal = context.Principal ?? throw new NullReferenceException(nameof(context.Principal));
userContext.Id = long.Parse(
principal.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.NameId)?.Value);
userContext.Username =
principal.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.UniqueName)?.Value!;
userContext.Name = principal.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Name)?.Value!;
userContext.Email = principal.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Email)?.Value!;
userContext.RoleIds = principal.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray();
userContext.RemoteIpAddress = context.HttpContext.GetRequestIp();
}
}

@ -0,0 +1,31 @@
namespace Infrastructure.Security;
public interface IUserContext
{
long Id { get; set; }
string Username { get; set; }
string Name { get; set; }
string Email { get; set; }
string[] RoleIds { get; set; }
string RemoteIpAddress { get; set; }
}
public class UserContext : IUserContext
{
public long Id { get; set; }
public string Username { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string[] RoleIds { get; set; }
public string RemoteIpAddress { get; set; }
}

@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
namespace Infrastructure.Security;
public class JwtBearerOptionsPostConfigureOptions(
DefaultTokenHandler tokenHandler,
ITokenBuilder tokenBuilder)
: IPostConfigureOptions<JwtBearerOptions>
{
public void PostConfigure(string? name, JwtBearerOptions options)
{
options.TokenHandlers.Clear();
options.TokenHandlers.Add(tokenHandler);
options.Events.OnTokenValidated = context =>
{
return Task.Run(() => { tokenBuilder.SetUserContext(context); });
};
}
}
Loading…
Cancel
Save