85 lines
3.8 KiB
C#
85 lines
3.8 KiB
C#
using IM.Commons;
|
||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.IdentityModel.Tokens;
|
||
using System.Text;
|
||
|
||
namespace IM.Jwt
|
||
{
|
||
public static class WebApplicationJwtExtension
|
||
{
|
||
public static IServiceCollection AddJwt(this IServiceCollection services, JwtOptions jwtOptions)
|
||
{
|
||
services.AddAuthentication(options =>
|
||
{
|
||
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||
})
|
||
.AddJwtBearer(options =>
|
||
{
|
||
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
|
||
{
|
||
ValidateIssuer = true,
|
||
ValidIssuer = jwtOptions.Issuer,
|
||
|
||
ValidateAudience = true,
|
||
ValidAudience = jwtOptions.Audience,
|
||
|
||
ValidateLifetime = true,
|
||
ClockSkew = TimeSpan.Zero,
|
||
|
||
// 验证签名秘钥(防止 Token 被篡改)
|
||
ValidateIssuerSigningKey = true,
|
||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.Key)),
|
||
};
|
||
options.Events = new JwtBearerEvents
|
||
{
|
||
OnMessageReceived = context =>
|
||
{
|
||
var accessToken = context.Request.Query["access_token"];
|
||
var path = context.HttpContext.Request.Path;
|
||
// SignalR WebSocket 握手时 token 通过 query string 传递(不是 Authorization 头)
|
||
// Hub 路径为 /chat(见 ConnectorService/Program.cs)
|
||
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/chat"))
|
||
{
|
||
context.Token = accessToken;
|
||
}
|
||
return Task.CompletedTask;
|
||
},
|
||
OnAuthenticationFailed = context =>
|
||
{
|
||
// 在这里打断点,查看 context.Exception
|
||
// 常见的有:SecurityTokenExpiredException (过期)
|
||
// 或 SecurityTokenInvalidSignatureException (密钥不对)
|
||
Console.WriteLine("验证失败原因: " + context.Exception.Message);
|
||
return Task.CompletedTask;
|
||
},
|
||
OnChallenge = async context =>
|
||
{
|
||
context.HandleResponse();
|
||
|
||
context.Response.ContentType = "application/json";
|
||
context.Response.StatusCode = StatusCodes.Status200OK;
|
||
|
||
var result = Result<object>.Fail(ResultCode.AUTH_FAILED);
|
||
await context.Response.WriteAsJsonAsync(result);
|
||
},
|
||
|
||
OnForbidden = async context =>
|
||
{
|
||
|
||
context.Response.ContentType = "application/json";
|
||
context.Response.StatusCode = StatusCodes.Status200OK;
|
||
var result = Result<object>.Fail(ResultCode.PERMISSION_DENIED);
|
||
await context.Response.WriteAsJsonAsync(result);
|
||
}
|
||
};
|
||
});
|
||
|
||
return services;
|
||
}
|
||
}
|
||
}
|