Falcon.SugarApi.ClaimTicket基本完成

This commit is contained in:
FalconFly 2024-05-07 11:43:05 +08:00
parent 206916e209
commit 3c08f01c19
5 changed files with 149 additions and 43 deletions

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Falcon.SugarApi.ClaimTicket namespace Falcon.SugarApi.ClaimTicket
@ -8,23 +9,31 @@ namespace Falcon.SugarApi.ClaimTicket
/// </summary> /// </summary>
public class ClaimTicketModelBinding:IModelBinder public class ClaimTicketModelBinding:IModelBinder
{ {
/// <summary>
/// 票据创建器
/// </summary>
public ITicketBuilder Builter { get; set; }
/// <summary> /// <summary>
/// 配置 /// 配置
/// </summary> /// </summary>
public ClaimTicketOptions Options { get; set; } public ClaimTicketOptions Options { get; set; }
/// <summary>
/// 票据创建器
/// </summary>
public ITicketBuilder TicketBuilder { get; }
/// <summary>
/// 日志记录
/// </summary>
public ILogger Logger { get; set; }
/// <summary> /// <summary>
/// 通过票据创建器创建模型绑定 /// 通过票据创建器创建模型绑定
/// </summary> /// </summary>
/// <param name="builter"></param> /// <param name="options">配置</param>
/// <param name="options"></param> /// <param name="ticketBuilder">票据创建器</param>
public ClaimTicketModelBinding(ITicketBuilder builter,ClaimTicketOptions options) { /// <param name="logger"></param>
Builter = builter; public ClaimTicketModelBinding(
ClaimTicketOptions options,
ITicketBuilder ticketBuilder,ILogger<ClaimTicketModelBinding> logger) {
this.Options = options; this.Options = options;
TicketBuilder = ticketBuilder;
this.Logger = logger;
} }
Task IModelBinder.BindModelAsync(ModelBindingContext bindingContext) { Task IModelBinder.BindModelAsync(ModelBindingContext bindingContext) {
@ -35,7 +44,14 @@ namespace Falcon.SugarApi.ClaimTicket
if(token.IsNullOrEmpty()) { if(token.IsNullOrEmpty()) {
token = bindingContext.HttpContext.Request.Headers[this.Options.HttpHeaderKey].ToString(); token = bindingContext.HttpContext.Request.Headers[this.Options.HttpHeaderKey].ToString();
} }
var model = this.Builter.GetUser(token); UserTicket? model;
try {
model = this.TicketBuilder.GetUser(token);
}
catch(System.Exception ex) {
this.Logger.LogError($"通过token生成用户时发生错误\n{ex}");
return FailBind(bindingContext);
}
if(model == null) { if(model == null) {
return FailBind(bindingContext); return FailBind(bindingContext);
} }

View File

@ -15,13 +15,5 @@ namespace Falcon.SugarApi.ClaimTicket
/// http头票据key /// http头票据key
/// </summary> /// </summary>
public string HttpHeaderKey { get; set; } = "_authUserTicket"; public string HttpHeaderKey { get; set; } = "_authUserTicket";
/// <summary>
/// 安全加密组件
/// </summary>
public IEncryption Encryption { get; set; }
/// <summary>
/// 序列化组件
/// </summary>
public ISerialize JsonSerialize { get; set; }
} }
} }

View File

@ -83,6 +83,8 @@ public string Viewbind(string _authUserTicket,[FromHeader] UserTicket user) {
一般客户端通过HTTP头参数_authUserTicket提交用户凭据这个例子中为了方便通过get参数提交票据控制器可以自动将其绑定在UserTicket对象中。 一般客户端通过HTTP头参数_authUserTicket提交用户凭据这个例子中为了方便通过get参数提交票据控制器可以自动将其绑定在UserTicket对象中。
因为action还通过httpbody获取了一个_authUserTicket参数所以UserTicket需要加[FromHeader]特性。 因为action还通过httpbody获取了一个_authUserTicket参数所以UserTicket需要加[FromHeader]特性。
## 完成以上工作基本就可以正常使用了,但是有时候我们还有一些特殊需求,可以通过下面的方式自行扩展。
### 6、自定义ClaimTicket配置 ### 6、自定义ClaimTicket配置
通过ClaimTicketOptions对象完成配置。完成配置的途径有两个。 通过ClaimTicketOptions对象完成配置。完成配置的途径有两个。
> 1、在插件配置中优先注入自己的ClaimTicketOptions对象。 > 1、在插件配置中优先注入自己的ClaimTicketOptions对象。
@ -102,7 +104,7 @@ public string Viewbind(string _authUserTicket,[FromHeader] UserTicket user) {
> } > }
> } > }
> ~~~ > ~~~
> 2、在插件配置中优先注入自己的ClaimTicketOptions对象 > 2、在services.AddClaimTicket中重写ClaimTicketOptions
> ~~~c# > ~~~c#
> public class ServicePlugin:IServicePlugin > public class ServicePlugin:IServicePlugin
> { > {
@ -178,3 +180,103 @@ public class ServicePlugin:IServicePlugin
} }
~~~ ~~~
### 8、扩展UserTicket
从UserTicket继承实现自己的用户类比如以下代码实现了自己的MyUserTicket类并添加了一个Ver属性。
~~~c#
public class MyUserTicket:UserTicket
{
public MyUserTicket() {}
public int Ver { get; set; }
public MyUserTicket(UserTicket? user) {
this.Claims = user?.Claims;
}
}
~~~
然后还需要实现自己的ITicketBuilder实现因为UserTicket不知道如何处理Ver。
~~~c#
/// <summary>
/// 自定义票据生成方式
/// </summary>
public class MyTicketBuilder:TicketBuilder, ITicketBuilder
{
/// <summary>
/// 使用票据参数构造生成器
/// </summary>
/// <param name="options">生成参数</param>
public MyTicketBuilder(ClaimTicketOptions options,IEncryption encryption,IJsonSerialize serialize)
: base(options,encryption,serialize) { }
/// <inheritdoc/>
public override string? GetTicket(UserTicket userTicket) {
//实现自己的票据生成方法
if(userTicket is MyUserTicket mut) {
var obj = mut.Claims.Select(a => new ClaimKeyValue { Key = a.Type,Value = a.Value });
var ll = obj.ToList();
ll.Add(new ClaimKeyValue { Key = "Ver",Value = mut.Ver.ToString() });
var str = this.Serialize.Serialize(ll);
var code = this.Encryption.Encrypt(this.Options.EncryptionKey,str);
return code;
}
//或使用基类提供的方法
return base.GetTicket(userTicket);
}
/// <inheritdoc/>
public override UserTicket? GetUser(string ticket) {
//根据票据获取用户声明返回用户信息
var str = this.Encryption.Decrypt(this.Options.EncryptionKey,ticket);
if(str.IsNullOrEmpty()) {
return new MyUserTicket();
}
var list = this.Serialize.Deserialize<List<ClaimKeyValue>>(str);
if(list == null) {
return new MyUserTicket();
}
var ut = new UserTicket(list.Select(a => new Claim(a.Key,a.Value)));
var result = new MyUserTicket(ut);
var verf = list.Find(a => a.Key == "Ver");
if(verf != null) {
result.Ver = int.Parse(verf.Value);
}
//或使用基类提供的方法
return result as UserTicket;
}
}
~~~
然后使用上面的方式把ITicketBuilder注入服务。
接着就是在登录时候使用自己的票据类
~~~c#
public string Login(string username) {
//验证用户密码有效性,自行调用数据库进行验证。
//如果通过验证,生成用户票据
var user = new UserTicket(
//添加用户名
new Claim("name",username),
//添加其他必要的声明
new Claim("role","admin")
);
var muser = new MyUserTicket(user);
//设置自己的特有属性
muser.Ver = 5;
var ticket = this.TicketBuilder.GetTicket(muser);
return ticket ?? "";
}
~~~
使用的时候可以直接注入:
~~~c#
public string Viewbind(string _authUserTicket,[FromHeader] MyUserTicket? user) {
if(user == null) {
return "绑定失败!";
}
StringBuilder sb = new();
foreach(var i in user.Claims) {
sb.AppendLine($"type:{i.Type},Val:{i.Value}");
}
sb.AppendLine($"user.Ver={user.Ver}");
return sb.ToString();
}
~~~

View File

@ -3,10 +3,6 @@ using Falcon.SugarApi.JsonSerialize;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Falcon.SugarApi.ClaimTicket namespace Falcon.SugarApi.ClaimTicket
{ {
@ -22,18 +18,15 @@ namespace Falcon.SugarApi.ClaimTicket
/// <param name="OptionBuilder">配置创建</param> /// <param name="OptionBuilder">配置创建</param>
/// <returns>服务集合</returns> /// <returns>服务集合</returns>
public static IServiceCollection AddClaimTicket(this IServiceCollection services,Action<ClaimTicketOptions>? OptionBuilder = null) { public static IServiceCollection AddClaimTicket(this IServiceCollection services,Action<ClaimTicketOptions>? OptionBuilder = null) {
services.TryAddSingleton<ClaimTicketOptions>(p => {
var en = p.GetRequiredService<IEncryption>();
var ser = p.GetRequiredService<IJsonSerialize>();
var option = new ClaimTicketOptions {
Encryption = en,JsonSerialize = ser,
};
OptionBuilder?.Invoke(option);
return option;
});
services.TryAddSingleton<IEncryption,AESProvider>(); services.TryAddSingleton<IEncryption,AESProvider>();
services.TryAddSingleton<IJsonSerialize,JsonSerialize.JsonSerialize>(); services.TryAddSingleton<IJsonSerialize,JsonSerialize.JsonSerialize>();
services.TryAddSingleton<ITicketBuilder,TicketBuilder>(); services.TryAddSingleton<ITicketBuilder,TicketBuilder>();
services.TryAddSingleton<ClaimTicketOptions>(p => {
var option = new ClaimTicketOptions();
OptionBuilder?.Invoke(option);
return option;
});
services.AddControllers(op => { services.AddControllers(op => {
op.ModelBinderProviders.Insert(0,new ClaimTicketModelBindingProvider()); op.ModelBinderProviders.Insert(0,new ClaimTicketModelBindingProvider());
}); });

View File

@ -1,4 +1,5 @@
using Falcon.SugarApi.Encryption; using Falcon.SugarApi.Encryption;
using Falcon.SugarApi.JsonSerialize;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
@ -13,24 +14,26 @@ namespace Falcon.SugarApi.ClaimTicket
/// <summary> /// <summary>
/// 构造票据生成器 /// 构造票据生成器
/// </summary> /// </summary>
/// <param name="options">加密配置</param> /// <param name="options">配置</param>
public TicketBuilder(ClaimTicketOptions options) { /// <param name="encryption">安全加密组件</param>
this.Encryption = options.Encryption; /// <param name="serialize">序列化组件</param>
this.Serialize = options.JsonSerialize; public TicketBuilder(ClaimTicketOptions options,IEncryption encryption,IJsonSerialize serialize) {
this.Options = options; this.Options = options;
Encryption = encryption;
Serialize = serialize;
} }
/// <summary> /// <summary>
/// 加密模块
/// </summary>
public IEncryption Encryption { get; }
/// <summary>
/// 序列化模块
/// </summary>
public ISerialize Serialize { get; }
/// <summary>
/// 票据生成配置 /// 票据生成配置
/// </summary> /// </summary>
public ClaimTicketOptions Options { get; } public ClaimTicketOptions Options { get; }
/// <summary>
/// 安全加密组件
/// </summary>
public IEncryption Encryption { get; }
/// <summary>
/// json序列化组件
/// </summary>
public ISerialize Serialize { get; }
/// <inheritdoc/> /// <inheritdoc/>
public virtual UserTicket? GetUser(string ticket) { public virtual UserTicket? GetUser(string ticket) {