系列文章的目录:https://blog.csdn.net/hjkl950217/article/details/89490709
记住:设计模式 重理解,轻照搬
目标
从这期文章开始,我会把每一篇文章想要达到的目标写在最前面,供您参考。
在一个检查类的内部,实现按不同的业务要求快速动态的构建检查方法,在C#中也就是委托。
知识要求
C#:懂C#中的委托、通Func这个预定义委托
Java:懂语法、懂接口定义和使用
其他:懂建造者模式的核心思想
背景
在近期的一个项目中我们需要做单点登录,除开登录服务、前端验证、分布式session的实现以前,还需要在程序中对每个API进行验证,Token编码使用JWT的方式。按我们的要求,需要在程序中实现4到5个检查点。其中还用到了多平台,平台代码需要从前端传递,程序中同样需要检查这一点。
目前要检查的点(API上有开启认证标签):
- 是否传递认证头
- 是否传递平台代码
- 认证头中的token值解码失败
- Token解码出来之后,sessionID为空或空字符串
- sessionID在分布式session网络中是否有效
以上5个点有一个检查不过,都会报401错误,并且在响应头中告诉它为什么。
认证流程中当然不止上面5个点,但其他的部分会由另一个认证框架来实现,这里我们就控制不到了。
分析问题
上面5个点都是必须验证通过的,不存在有某个需求后可以跳过后面的步骤,所以不考虑执行顺序。
另外一点,以后有没有添加新的认证需求呢?比如加入IP黑白名单?不使用JWT,使用别的传输方式?强制下线?
所以这里我的设计思路是:
- 在执行时调用一个方法来完成认证
- 这个方法中调用不同的检查委托来认证
- 检查委托是通过Func<T1,T2>这种模式添加到一个集合中的, 2中的检查方法只是遍历这个集合
前面我们知道建造者模式是为了动态的去构建一个对象。那么在现在这个业务场景中,能不能把检查方法看成我们要构建的对象呢?我认为是可以的。 因为检查方法主要是遍历Func这个集合,那么让向集合添加检查委托这一步动态起来就好了。
核心代码
1.定义最后调用的东西
2.编写添加子检查的代码
3.构造方法中控制调用那些子检查。
定义检查委托集合
首先,会定义一个检查委托集合,检查方法会遍历执行它。
private readonly List<Func<HttpContext, BizValidationError>> ChecFunckList;//BizValidationError是我们自定义的一个错误消息类,你也可以用你的
调用:
foreach (var item in this.ChecFunckList)
{
BizValidationError validationError = item(httpContext);
if (validationError != null)
{
httpContext.Response.Headers["YourHeaderName"] = validationError.ErrorMessage;
return validationError.CustomObject as IActionResult;
}
}
编写子委托的代码
以检查平台为例:
public void AddPlatformCodeCheckFun()
{
Func<HttpContext, BizValidationError> checkFunc = t =>
{
string platformCode = t.Request.Headers["PlatformCode"];
if(platformCode==null|| platformCode.Length == 0)
{
return new BizValidationError()
{
CustomObject = new UnauthorizedResult(),//需求中提到没传平台也当未认证处理
ErrorMessage = $"PlatformCode error"
};
}
else
{
return null
}
};
}
构造方法中控制调用那些检查
public AuthorizeAttribute()
{
// 1.初始化对象
this.ChecFunckList = new List<Func<HttpContext, BizValidationError>>();
// 2.在这里控制添加了那些检查
this.AddPlatformCodeCheckFun();
//下面的方式与上面类似,只是具体代码不一样,就没有再列出来了
this.AddJwtTokenExistFunc();
this.AddRedisValueExistFunc();
this.AddSessionIdExistFunc();
}
动态的关键点
关键点在于检查集合中的内容,是动态添加的。只要控制了这点,不管你怎么玩,都能很轻松的实现目标。
Java中怎么写?
C#中是利用委托来实现,而java中不像C#中有那样灵活的委托、内部方法这些,只能使用抽象或接口来实现了。
编写逻辑:定义抽象检查方法->多个子类实现->有一个地方统一向检查集合中添加这些子类实例
真-动态
核心就在于怎么让检查集合获得这些委托,或者说“什么情况下添加什么委托”。 这一点我们可以使用配置文件或配置系统实现了。
例:代码中默认添加所有的逻辑进去,但是会按配置是否跳过这个检查。
public AuthorizeAttribute()
{
// 1.初始化对象
this.ChecFunckList = new List<Func<HttpContext, BizValidationError>>();
// 2.在这里控制添加了那些检查
this.AddPlatformCodeCheckFun();
//下面的方式与上面类似,只是具体代码不一样,就没有再列出来了
this.AddJwtTokenExistFunc();
this.AddRedisValueExistFunc();
this.AddSessionIdExistFunc();
//按配置动态添加
if(config.CheckIp)
{
this.AddIpCheckFunc();
}
}
还有其它的玩法,随便怎么玩~
总结
文章核心思想就是让我们的逻辑像变量一样,动态的增加进去。这一点和建造者模式的思想是比较类似的,我这里也是从建造者模式中得到启发的。 我这个业务场景中不存在要跳逻辑的情况,如果有,更应该使用责任链模式一些。
如果大家有更好的想法,欢迎在下面留言讨论