更新JWT

This commit is contained in:
chfei committed 2026-10-23 19:28:17 +08:00
1 parent bb7162aaf6
commit 3769193696
16 files changed
+224 -141

No files matched your search

@@ -0,0 +1,77 @@
using System.Security.Claims;
using MAX.Framework.Security;
using Microsoft.Extensions.Options;
namespace Store.Users.Tests;
public sealed class PersistentSessionTests
{
[Fact]
public async Task TokensSurviveRestart_AndLogoutOnlyRevokesCurrentSession()
{
var path = Path.Combine(Path.GetTempPath(), $"sessions-{Guid.NewGuid():N}.db");
var options = Options.Create(new JwtOptions());
TokenResult first;
TokenResult second;
string firstJti;
try
{
using (var db = Open(path))
{
db.CodeFirst.SyncStructure<OnlineUserInfo>();
var store = new OnlineUserStore(db);
var tokens = new TokenService(options, store);
first = tokens.CreateToken([new Claim(JwtClaimTypes.UserName, "same")], "192.0.2.10");
second = tokens.CreateToken([new Claim(JwtClaimTypes.UserName, "same")], "2001:db8::1");
firstJti = (await tokens.ValidateTokenAsync(first.Token))!.FindFirst(JwtClaimTypes.Jti)!.Value;
Assert.NotNull(await tokens.ValidateTokenAsync(second.Token));
Assert.Equal(2, store.Count);
}
// 新 ORM、新存储和 TokenService,模拟进程重启后的数据库恢复。
using (var db = Open(path))
{
var store = new OnlineUserStore(db);
var tokens = new TokenService(options, store);
Assert.NotNull(await tokens.ValidateTokenAsync(first.Token));
Assert.NotNull(await tokens.ValidateTokenAsync(second.Token));
Assert.Contains(store.GetOnlineUsers(), x => x.LoginIp == "2001:db8::1");
store.SignOutSession("other", firstJti);
Assert.NotNull(await tokens.ValidateTokenAsync(first.Token));
store.SignOutSession("same", firstJti);
Assert.Null(await tokens.ValidateTokenAsync(first.Token));
Assert.NotNull(await tokens.ValidateTokenAsync(second.Token));
Assert.Single(store.GetOnlineUsers());
Assert.NotNull(db.Select<OnlineUserInfo>().Where(x => x.Jti == firstJti).First().RevokedAt);
}
using (var db = Open(path))
{
var store = new OnlineUserStore(db);
var tokens = new TokenService(options, store);
Assert.Null(await tokens.ValidateTokenAsync(first.Token));
Assert.NotNull(await tokens.ValidateTokenAsync(second.Token));
store.SignOut("same");
Assert.Null(await tokens.ValidateTokenAsync(second.Token));
}
}
finally { File.Delete(path); }
}
[Fact]
public async Task ExpiredSessionsAreRejected_EvenWithinJwtClockTolerance()
{
using var db = Open(":memory:");
db.CodeFirst.SyncStructure<OnlineUserInfo>();
var store = new OnlineUserStore(db);
var tokens = new TokenService(Options.Create(new JwtOptions()), store);
var token = tokens.CreateToken([new Claim(JwtClaimTypes.UserName, "user")]);
db.Update<OnlineUserInfo>().Set(x => x.ExpiresAt, DateTime.UtcNow.AddSeconds(-1))
.Where(x => x.UserName == "user").ExecuteAffrows();
Assert.Null(await tokens.ValidateTokenAsync(token.Token));
Assert.Empty(store.GetOnlineUsers());
Assert.Equal(1, db.Select<OnlineUserInfo>().Count());
}
private static IFreeSql Open(string path) => new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, $"Data Source={path};Pooling=false")
.Build();
}
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><IsTestProject>true</IsTestProject><IsPackable>false</IsPackable></PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../Core/MAX.Framework/MAX.Framework.csproj" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="10.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5"><PrivateAssets>all</PrivateAssets></PackageReference>
<PackageReference Include="FreeSql.Provider.Sqlite" Version="3.5.311" />
<Using Include="Xunit" />
<Compile Include="../Store.Users.Tests/DevelopmentTokenTests.cs" Link="DevelopmentTokenTests.cs" />
</ItemGroup>
</Project>