对于defender
的定义,我们将之抽象为一个城堡守卫阵法,如果要守护城堡,并不可少需要守护者的参与,而guarder
就是所谓的守护者。
一个服务之中只会有一个defender
,与之息息相关的guarder
会和defender
形成MANY-TO-ONE
的关系,每个guarder
都可以指定不同的模式和策略去抵御或者说是过滤访客和外敌,对于多个guarder
同时运作,他们之间将会相互合作,为服务的安全性一起努力工作!
defender
为一个单例对象,我们可以通过以下方式获取它
Defender.getInstance()
defender
更像是一个容器,它装载着我们定义的各种guarder
来帮助我们进行权限管理,我们可以通过调用registry
方法去注册一个guarder
Defender.getInstance()
.registry(guarder)
.ready()
可以看出,在注册完guarder
之后,我们调用了ready
方法,这是必不可少的,这是我们通知defender
一切准备就绪信号,一旦执行此方法,defender
将会进行一起准备的初始化工作,此方法执行完之后,defender
的一切准备工作告一段落!
获取一个Guarder很简单,它的构造被私有化,以来使初始化的过程更加优雅,下面是一个完整的guarder
实例化过程
Guarder.builder(GuarderType.URI)
.pattern("POST /user")
.order(1)
.preventer(caller -> {
return Result.pass();
})
其中的order
是可以缺省的,有了它你可以自定义多个guarder
防御的顺序,最前面的可以称之为敢死队。在调用builder
方法的时候,我们需要传入一个枚举值来表示采用的防御模式,这个之后我们将会详解。从此可以看出,获取一个guarder
对象也是如此的简单。
defender
提供三种防御模式,对于不同的防御模式配置风格不尽相同
虽然与众不同,但是可以项目配合着使用,以下我们将会进一步深入了解每种模式
使用注解模式,我们需要实例化一个对应的guarder
Guarder.builder(GuarderType.ANNOTATION)
.pattern("org.nico.trap.controller")
.preventer(caller -> {
return Result.pass();
})
紧接着,我们还需要在需要防御的方法上添加@Access
注解
@Access(AuthConst.LOGIN)
@PostMapping("/")
public ResponseVo<GameFullVo> publishGame(@RequestBody GamePublishVo gameVo) throws TrapException{
GameFullVo gameFullVo = gameService.publishGame(gameVo);
return new ResponseVo<GameFullVo>(ResponseCode.SUCCESS, gameFullVo);
}
@Access
注解内传入的value值可以根据当前系统的情况单独定义一个常量,defender
并没有做强定义!对于每种模式下的pattern
的格式不同,对于注解模式,pattern
的意义就是代表着包名,对于该包下的所有带有@Access
都将会进行防御。
表达式模式相比注解模式,粒度变得更大,但是通用性也会变大,我们可以通过一下方式获得一个Expression模式的guarder
Guarder.builder(GuarderType.EXPRESSION)
.pattern("* org.nico.trap.controller.UserController.*(..)")
.preventer(caller -> {
return Result.pass();
})
与注解模式不同的是,pattern
的格式和所代表的的含义变化很大,这里表示一个execution表达式
。
URI模式则更为直观
Guarder.builder(GuarderType.URI)
.pattern("POST /user")
.preventer(caller -> {
return Result.pass();
})
pattern
中分两段,第一段为请求类型,后一段为请求资源地址,匹配方式使用ANT
风格,整体与Expression模式类似,不同的是,Expression模式拦截的是方法,URI模式拦截的是接口。
之前的讲述中,我们知道了defender
和guarder
的联系和作用,也知道了guarder
的各种防御模式,下面我们将会对guarder
进入更深层次的剖析,这样才能更好更灵活的使用guarder
。
该方法需要传入一个枚举值GuarderType
去区分guarder
的防御模式,返回值将会是一个guarder
对象,也是唯一实例化的入口!
pattern
的含义随着防御模式的不同而变化着,对于同一个guarder
,可以有多个pattern
,如在URI模式下可以写成
Guarder.builder(GuarderType.URI)
.pattern("POST /user")
.pattern("DELETE /user")
.pattern("* /game")
.preventer(caller -> {
return Result.pass();
})
order
方法指定一个int
值表示defender
下所有guarder
的防御顺序,order
值越小优先级越高,可缺省,默认值为0。
该方法传入一个AbstractPreventer
接口对象,我们需要实现该接口内部的detection
方法去自定义防御规则
public interface AbstractPreventer {
public Result detection(Caller caller);
}
接下来我们拟定一个场景:需要获取请求头里的token
来判断用户是否是在登录状态下的防御,这里我们使用注解模式进行防御来构造一个guarder
对象
Guarder.builder(GuarderType.ANNOTATION)
.pattern("org.nico.trap.controller")
.preventer(new AbstractPreventer() {
@Autowired
private AuthComponent authComponent;
@Override
public Result detection(Caller caller) {
String identity = caller.getAccess().value();
if(! identity.equals(AuthConst.VISITOR)) {
UserBo user = authComponent.getUser();
if(user != null) {
if(identity.equals(AuthConst.ADMIN)){
if(user.getRuleType() == null) {
return Result.notpass(new ResponseVo<>(ResponseCode.ERROR_ON_USER_IDENTITY_MISMATCH));
}
}
}else {
return Result.notpass(new ResponseVo<>(ResponseCode.ERROR_ON_LOGIN_INVALID));
}
}
return Result.pass();
}
})
上述的AbstractPreventer
实现类中,我们用到了@Autowired
,这是defender
必须支持的功能,而对于简单的防御,我们可以使用lambda
更加简洁的实现,如判断请求头token
是否存在
Guarder.builder(GuarderType.ANNOTATION)
.pattern("org.nico.trap.controller")
.preventer(caller -> {
return caller.getRequest().getHeader("token") == null
? Result.notpass("Token not exist")
: Result.pass();
})
从上述例子可以看出,defender
方法的返回值分两种类型:pass
和notpass
,通过Result
的静态方法获取对应的实例
Result.notpass(Object o)
表示未穿破防御,这个请求时非法的,传入的参数o
代表返回给请求者的提示信息。Result.pass()
表示合法请求此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。