Falcon.SugarApi/Falcon.SugarApi/ClaimTicket
2024-05-07 11:43:05 +08:00
..
ClaimKeyValue.cs ClaimKeyValue 模块初步 1 2024-04-30 15:54:01 +08:00
ClaimTicketModelBinding.cs Falcon.SugarApi.ClaimTicket基本完成 2024-05-07 11:43:05 +08:00
ClaimTicketModelBindingProvider.cs 自定义用户票据 2024-05-06 17:24:29 +08:00
ClaimTicketOptions.cs Falcon.SugarApi.ClaimTicket基本完成 2024-05-07 11:43:05 +08:00
ITicketBuilder.cs ClaimKeyValue 模块初步 1 2024-04-30 15:54:01 +08:00
Readme.md Falcon.SugarApi.ClaimTicket基本完成 2024-05-07 11:43:05 +08:00
ServiceCollectionExtend.cs Falcon.SugarApi.ClaimTicket基本完成 2024-05-07 11:43:05 +08:00
TicketBuilder.cs Falcon.SugarApi.ClaimTicket基本完成 2024-05-07 11:43:05 +08:00
UserTicket.cs ClaimKeyValue 模块初步 1 2024-04-30 15:54:01 +08:00

客户凭据 Falcon.SugarApi.ClaimTicket.ClaimTicket

1、添加ClaimTicket支持。

是否使用ClaimTicket由插件自行决定。
如果插件需要使用ClaimTicket按如下方式在插件服务配置中添加。

public class ServicePlugin:IServicePlugin
{
    IServiceCollection IServicePlugin.AddServices(IServiceCollection services,IConfiguration configuration) {
        //注入使用ClaimTicket的控制器
        services.AddPluginsController(this.GetType().Assembly);
        //添加ClaimTicket服务支持
        services.AddClaimTicket();
        return services;
    }
}

2、控制器注入

要完成用户的登录认证和绑定工作需要注入ITicketBuilder接口这个接口在上面的services.AddClaimTicket()中已经完成注入。
下面是在HomeController控制器中注入ITicketBuilder的示例。


public ITicketBuilder TicketBuilder { get; set; }

public HomeController(IServiceProvider service) : base(service) {
    this.TicketBuilder = service.GetRequiredService<ITicketBuilder>();
}

3、用户登录。

插件需要自行完成用户认证工作,示例代码如下。

[HttpGet]
public string Login(string username) {
    //验证用户有效性,自行调用数据库进行验证。

    //如果通过验证,生成用户票据
    var user = new UserTicket(
            //添加用户名
            new Claim("name",username),
            //添加其他必要的声明
            new Claim("role","admin")
    );
    var ticket = this.TicketBuilder.GetTicket(user);
    return ticket ?? "";
}

客户端在收到返回的票据后自行保管票据。

4、验证用户

实例代码如下:

[HttpGet]
public string ViewUser(string ticket) {
    //通过票据获取用户
    var cs = this.TicketBuilder.GetUser(ticket);
    if(cs == null) {
        return "TicketBuilder.GetUser 返回空";
    }
    StringBuilder sb = new();
    //轮训票据生成获取登录信息
    foreach(var i in cs.Claims) {
        sb.AppendLine($"type:{i.Type},Val:{i.Value}");
    }
    return sb.ToString();
}

5、绑定用户

示例代码如下:

[HttpGet]
public string Viewbind(string _authUserTicket,[FromHeader] UserTicket user) {
    StringBuilder sb = new();
    foreach(var i in user.Claims) {
        sb.AppendLine($"type:{i.Type},Val:{i.Value}");
    }
    return sb.ToString();
}

一般客户端通过HTTP头参数_authUserTicket提交用户凭据这个例子中为了方便通过get参数提交票据控制器可以自动将其绑定在UserTicket对象中。
因为action还通过httpbody获取了一个_authUserTicket参数所以UserTicket需要加[FromHeader]特性。

完成以上工作基本就可以正常使用了,但是有时候我们还有一些特殊需求,可以通过下面的方式自行扩展。

6、自定义ClaimTicket配置

通过ClaimTicketOptions对象完成配置。完成配置的途径有两个。

1、在插件配置中优先注入自己的ClaimTicketOptions对象。

public class ServicePlugin:IServicePlugin
{
    IServiceCollection IServicePlugin.AddServices(IServiceCollection services,IConfiguration configuration) {
        //注入使用ClaimTicket的控制器
        services.AddPluginsController(this.GetType().Assembly);

        //优先添加自己的服务配置
        services.AddSingleton(new ClaimTicketOptions());

        //添加ClaimTicket服务支持
        services.AddClaimTicket();
        return services;
    }
}

2、在services.AddClaimTicket中重写ClaimTicketOptions。

public class ServicePlugin:IServicePlugin
{
    IServiceCollection IServicePlugin.AddServices(IServiceCollection services,IConfiguration configuration) {
        //注入使用ClaimTicket的控制器
        services.AddPluginsController(this.GetType().Assembly);

        //优先添加自己的服务配置
        //services.AddSingleton(new ClaimTicketOptions());

        //添加ClaimTicket服务支持
        services.AddClaimTicket(optionsBuilder => {
            //客户端通过_headCalimTick属性提交凭据
            optionsBuilder.HttpHeaderKey = "_headCalimTick";
        });
        return services;
    }
}

7、扩展ITicketBuilder

插件可以自行扩展票据的生成方式方法有两个一个是继承TicketBuilder对象并重写方法另一个就是自己实现ITicketBuilder接口。

/// <summary>
/// 自定义票据生成方式
/// </summary>
public class MyTicketBuilder:TicketBuilder, ITicketBuilder
{
    /// <summary>
    /// 使用票据参数构造生成器
    /// </summary>
    /// <param name="options">生成参数</param>
    public MyTicketBuilder(ClaimTicketOptions options) : base(options) { }

    /// <inheritdoc/>
    public override string? GetTicket(UserTicket userTicket) {
        //实现自己的票据生成方法
            
        //或使用基类提供的方法
        return base.GetTicket(userTicket);
    }

    /// <inheritdoc/>
    public override UserTicket? GetUser(string ticket) {
        //根据票据获取用户声明返回用户信息

        //或使用基类提供的方法
        return base.GetUser(ticket);
    }
}

然后在调用AddClaimTicket之前注入自己的ITicketBuilder

public class ServicePlugin:IServicePlugin
{
    IServiceCollection IServicePlugin.AddServices(IServiceCollection services,IConfiguration configuration) {
        //注入使用ClaimTicket的控制器
        services.AddPluginsController(this.GetType().Assembly);

        //优先添加自己的服务配置
        //services.AddSingleton(new ClaimTicketOptions());

        //注入扩展MyTicketBuilder
        services.TryAddSingleton<ITicketBuilder,MyTicketBuilder>();

        //添加ClaimTicket服务支持
        services.AddClaimTicket(optionsBuilder => {
            //客户端通过_headCalimTick属性提交凭据
            optionsBuilder.HttpHeaderKey = "_headCalimTick";
        });
        return services;
    }
}

8、扩展UserTicket

从UserTicket继承实现自己的用户类比如以下代码实现了自己的MyUserTicket类并添加了一个Ver属性。

public class MyUserTicket:UserTicket
{
    public MyUserTicket() {}

    public int Ver { get; set; }

    public MyUserTicket(UserTicket? user) {
        this.Claims = user?.Claims;
    }
}

然后还需要实现自己的ITicketBuilder实现因为UserTicket不知道如何处理Ver。

/// <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注入服务。
接着就是在登录时候使用自己的票据类

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 ?? "";
}

使用的时候可以直接注入:

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();
}