【pig-cloud项目】关于@Inner和@PreAuthorize的理解,以及微服务内外部间的调用认证鉴权理解

学习pig-cloud项目时遇到了微服务内外部间的调用认证鉴权理解这个问题,总理解不了文档Inner注解使用说明 · 语雀中的文字,然后看过源码,并用代码测试后,理解并总结一下,怕自己忘记,脑子不够用~ (ಥ﹏ಥ)

文章中没有代码说明,只是逻辑理解,代码可以看这个系列下的文章:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客_pig项目学习

目录

A1.微服务内外部间的调用认证鉴权理解

B1.个人理解

B2.pig项目中的实现

A2.关于@Inner和@PreAuthorize的理解(只基于pig项目代码)

B1.举个例子理解

B2.结论:被 @Inner(true/false) 注释的接口不能拿到用户认证信息

B3.@Inner注解内部调用安全性问题

A3.修改 pig 微服务内部调用认证鉴权逻辑


A1.微服务内外部间的调用认证鉴权理解

B1.个人理解

首先,先不看pig 项目,就单纯从微服务内外部间的调用鉴权来说,通常有两点是需要保证的:

1. 内部服务间Feign访问永远都不需要认证鉴权,但可以传递认证信息;(没遇到过内部服务之间调用需要鉴权的情况)
2. 外部访问通过Gateway访问,不会也不应该直接访问某服务;(没遇到过微服务场景下直接访问某服务的情况)

基于以上两点和正常开发,会有以下几点调用接口的应用场景:

1. 外部从Gateway访问、外部直接访问某服务和内部服务间Feign访问,都不需要认证鉴权;
2. 外部从Gateway访问需要认证鉴权;外部不能直接访问某服务;内部服务间Feign访问不需要认证鉴权;
3. 外部从Gateway访问需要认证鉴权;外部直接访问某服务需要鉴权;内部服务间Feign访问不需要认证鉴权;

场景 3. 是针对场景 2. 的一个补充,外部直接访问某服务需要认证鉴权的情况也应该是存在的。

【没看明白我的理解,可以看 pig 文档中的:Inner注解使用说明 · 语雀,不知道为什么我是没理解他的说明,我是从源代码里看明白后才看懂的 o(╥﹏╥)o】

B2.pig项目中的实现

明白微服务内外部间的调用认证鉴权的场景后,再来看 pig 项目的实现,这个项目中的逻辑是实现了上面的场景 1.2. ,第三点没有实现。

项目中 1.2. 场景的代码实现逻辑是:

1.如果是全部不需要认证鉴权,那么各个服务中只需要将这个接口添加到security过滤链的permitAll白名单即可;

2.外部从Gateway访问需要认证鉴权的情况,就按照正常的授权端给服务添加过滤器(BearerTokenAuthenticationFilter)和鉴权(鉴权注解@PreAuthorize)就可以。

而如果再加上外部不能直接访问某服务,并且内部服务间Feign访问不需要认证鉴权,的情况,就会在启动项目时先将该接口动态的添加到security过滤链的permitAll白名单,和校验accesstoken的白名单(BearerTokenResolver)中,然后在调用接口时直接通过security过滤链,然后通过注解@Inner的value值判断是否是内部调用,true则表示是内部调用,false表示不是内部调用。

但是由于此接口已经是白名单不鉴权了,为了防止外部直接访问,于是添加了一个@RequestHeader(SecurityConstants.FROM)参数值的来校验是内部调用,也就是说当@Inner的value=true,并且@RequestHeader(SecurityConstants.FROM)的参数值能够匹配上我们定义的值(相当于是密码了),就能够直接访问;如果@Inner的value=false,则相当于接口完全对外暴露了。

同时 pig 项目还在gateway网关里面添加了一个 header 过滤,会将header=From的参数值去掉,没太明白这个步骤的意义,这样仅仅是能够防止通过网关来调用仅能够内部调用的接口,如果定义的header=From的参数值暴露了,用户依旧可以通过添加header=From值来调用@Inner(value=true)的接口。

A2.关于@Inner和@PreAuthorize的理解(只基于pig项目代码)

B1.举个例子理解

OK,看完了理论,我们就来看一下实践,同时,再理解一下@Inner和@PreAuthorize这两个注解~

先来举个例子:

//以 pig-cloud 项目代码为例,看 pig-upms-biz 中的以下接口添加注解情况:

/*
*    1.这种情况下:
*    a.通过gateway网关访问时,自动去掉header=From的参数值,由于没有header=From值,会返回没有访问权限;
*    b.直接访问服务时,如果header=From值错误,则返回没有访问权限;如果header=From值正确,则访问成功;
*    c.服务间内部调用时,自动携带header=From的正确参数值,会访问成功;
*/
	@Inner
	@GetMapping("/ids")
	public R> listUserIdByDeptIds(@RequestParam("deptIds") Set deptIds) {
		return R.ok(userService.listUserIdByDeptIds(deptIds));
	}

/*
*    2.这种情况下:
*    d.通过gateway网关访问时,自动去掉header=From的参数值,由于@Inner的value=false,直接访问成功;
*    e.直接访问服务时,由于@Inner的value=false,直接访问成功;
*    f.服务间内部调用时,无论是否携带header=From的参数值,由于@Inner的value=false,直接访问成功;
*/
	@Inner(false)
	@GetMapping("/ids")
	public R> listUserIdByDeptIds(@RequestParam("deptIds") Set deptIds) {
		return R.ok(userService.listUserIdByDeptIds(deptIds));
	}

/*
*    3.这种情况下:
*    无论用哪种方式访问,都会返回没有访问权限!
*    原因:首先先要知道@Inner和@PreAuthorize注解都是有个切面的,并且@Inner的切面执行顺序在@PreAuthorize的切面之前。当前请求在上面的 b.c.d.e.f 情况中都能够顺利通过@Inner注解的切面,但是在@PreAuthorize的切面里面永远都不会通过!因为永远拿不到已认证的SecurityContextHolder.getContext().getAuthentication()!
*/
	@Inner(true/false)
    @PreAuthorize("@pms.hasPermission('任意值权限标识')")
	@GetMapping("/ids")
	public R> listUserIdByDeptIds(@RequestParam("deptIds") Set deptIds) {
		return R.ok(userService.listUserIdByDeptIds(deptIds));
	}

上面的例子 3. 中涉及到了已认证用户的鉴权逻辑。

pig 项目中资源端是使用spring-security-oauth2-resource-server来实现用户认证的。他的逻辑是,当有个请求到来时,先通过BearerTokenAuthenticationFilter过滤器来拿到请求中的token,然后带着token去向授权端校验token并且拿到认证的用户信息,然后将用户信息放入自己的SecurityContextHolder里面以方便请求后面的业务使用。

这里在BearerTokenAuthenticationFilter里面,会先通过一个 Resolver 来拿到 token ,拿到就向授权端发起认证,没有拿到就执行下一个过滤器。而 pig 项目自定义了一个这个 Resolver 的实现类!并且里面有个逻辑是,如果当前请求是白名单就直接返回null!

看到这里就明白了,在资源端中(授权端是没有配置resource的)只要被 @Inner 注解的接口,就永远都不会有用户认证的信息!

图解,防忘记~

【pig-cloud项目】关于@Inner和@PreAuthorize的理解,以及微服务内外部间的调用认证鉴权理解_第1张图片

B2.结论:被 @Inner(true/false) 注释的接口不能拿到用户认证信息

@Inner:保证服务间内部调用不鉴权(调用要加正确的header),并且保证通过网关访问服务一律无权限,并且保证直接访问服务一律无权限(只能保证正常情况下,如果请求携带header并且值正确就会访问成功的【这是属于漏洞吧,我在pig的仓库中也有看到问这个安全问题的,lengleng回复是:“最小化运维原则 不会暴露 除这些端口以外的其他端口”,具体可以看这个issue:Inner注解内部调用安全性问题 · Issue #I5S2U9 · lengleng/pig - Gitee.com】)

@Inner(flase):任意情况下都能够访问成功;

但要记住,被 @Inner(true/false) 注释的接口都拿不到用户认证信息!所以@Inner和@PreAuthorize一起使用是永远返回无权限的!

授权端不会用到@PreAuthorize注解的。

但是可能会用到@Inner,以防止有服务会调用授权端的接口,并且不让外部进行调用。

B3.@Inner注解内部调用安全性问题

加个目录,小心忘记了 (〝▼皿▼)

pig的仓库中也有看到问这个安全问题的,lengleng回复是:“最小化运维原则 不会暴露 除这些端口以外的其他端口”,具体可以看这个issue:Inner注解内部调用安全性问题 · Issue #I5S2U9 · lengleng/pig - Gitee.com】)

A3.修改 pig 微服务内部调用认证鉴权逻辑

pig 中无法实现 3. 外部从Gateway访问需要认证鉴权;外部直接访问某服务需要鉴权;内部服务间Feign访问不需要认证鉴权;

但是我们可以修改他的逻辑,在 1.2. 的基础上实现 3. (但是还是有个漏洞!)。

三个步骤:

1.首先将 @Inner 主机的切面去掉,不再需要这个切面了,@Inner 的逻辑就只是作为一个标识,表示这个接口有用于微服务内容调用而已。 

2.修改 PermitAllUrlProperties 白名单配置类,修改现有的 urls 属性只添加配置项的路径:此类接口皆可不鉴权访问;再添加一个 innerUrls 属性只添加被 @Inner 标注的接口路径:此类接口专门给内部调用不鉴权访问;(也就是将 afterPropertiesSet() 里面的动态拦截路径添加到 innerUrls 里面)

3.再修改 PigBearerTokenExtractor 类,如果请求匹配中 PermitAllUrlProperties 的 urls 属性,就直接放行不认证;如果请求匹配中 PermitAllUrlProperties 的 innerUrls 属性,并且有 header(From)==正确值,就相当于内部调用就直接不鉴权放行;如果请求匹配中 PermitAllUrlProperties 的 innerUrls 属性,并且有 header(From)!=正确值,就相当于非内部调用就必须鉴权,鉴权失败就不放行;(这种情况下,微服务内部调用也可以不携带header(From),而是携带token就可以认证)

警告!警告!警告!这种情况下如果 header(From) 参数值暴露了,就能直接不鉴权访问了!!!

当然也可以微服务内部时全部鉴权,那上面的逻辑就都不用了,只需要内部调用添加 token 就行~

看实际业务环境吧

你可能感兴趣的:(pig学习,oauth2,微服务,架构,spring,boot)