nacos源码之Auth(权限)模块-1(授权过滤器与控制器缓存)
Posted keep-go-on
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nacos源码之Auth(权限)模块-1(授权过滤器与控制器缓存)相关的知识,希望对你有一定的参考价值。
下一篇:《nacos源码之Auth(权限)模块-2(权限管理与权限配置)》
上一篇《nacos源码构建与总览》浏览器还挺多,接下来模块的精讲来了
Nacos的Auth模块
auth模块管理了nacos的权限,该权限系统设计面向租户模式,功能比较简单,不适用于业务系统。
授权过滤器(权限核心注解)
源码如下:
package com.alibaba.nacos.auth.annotation;
@Retention(RetentionPolicy.RUNTIME)
public @interface Secured {
// 操作类型(nacos只定义了读和写两种操作类型)
ActionTypes action() default ActionTypes.READ;
// 操作的资源
String resource() default StringUtils.EMPTY;
Class<? extends ResourceParser> parser() default DefaultResourceParser.class;
}
nacos 权限模块只定义了两种操作类型,
读
和写
。但从这个角度也无法胜任业务系统的权限框架,但是对于服务发现,配置管理视乎也够用了。
在一个通用的权限框架中,权限框架只需要管理权限资源的操作类型是否通过即可,这一点Nacos是相同的。
注解实现讲解
这里就是整个权限模块的核心了。
该注解的实现并不在auth包下,但是注解的实现也是属于权限模块的,因此在本章节讲解。
看这个代码之前,先强调一些知识,帮助理解nacos的权限实现 :
- nacos使用spring mvc架构。因此它是
servlet
,并不是netty的非阻塞框架。 - nacos注解使用过滤器实现。
netty
与servlet
并没有优略之分,它们两个亦不是水火不容,使用netty
还是使用servlet
,需要根据解决问题的场景参考,它们本身就是为服务实现提出的不同的解决方案。
由于代码篇幅过长,带有备注的代码已经提交到Github ,请见 :https://github.com/keepgoon/nacos/blob/develop/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java
核心片段:
// 获取缓存方法
Method method = methodsCache.getMethod(req);
// 没有匹配到方法,说明未被权限模块扫描,直接放行
if (method == null) {
chain.doFilter(request, response);
return;
}
// 如果含有权限注解才去进行权限校验
if (method.isAnnotationPresent(Secured.class) && authConfigs.isAuthEnabled()) {
Secured secured = method.getAnnotation(Secured.class);
String action = secured.action().toString();
String resource = secured.resource();
if (StringUtils.isBlank(resource)) {
ResourceParser parser = getResourceParser(secured.parser());
resource = parser.parseName(req);
}
if (StringUtils.isBlank(resource)) {
// 如果没有匹配到资源直接报权限异常
throw new AccessException("resource name invalid!");
}
// 进行授权校验,不通过就抛出AccessException
authManager.auth(new Permission(resource, action), authManager.login(req));
}
注解的核心实现在集成的doFilter
方法中实现,随便吐槽一下,这里面的代码似乎并不是特别优雅。
小知识来了 :
授权过滤器中使用了
ConcurrentHashMap
,这个集合并发度
是比较牛的。这里不讲多线程,粗略的说一下原理,HashTable使用的是synchronized
关键字,是对整个对象加锁,而ConcurrentHashMap
采用了分段锁。有兴趣的可以评论区留言
实现逻辑:
- 首先判断是否开启权限控制。这个功能在权限配置中实现,具体见下面的
权限配置
。 - 然后判断了是否开启白名单。处理方式也很简单,直接交给其他过滤器解决。但是还是加了些判断,判断请求头里是否含有
User-Agent
属性,有的话才去交给另一个过滤器执行。 - 第三个判断就判断了是否含有授权Key,如果请求头里面含有授权Key,就和配置的授权信息比较,如果相同就交给其他过滤器执行。
- 如果啥都没有,就会执行到
403
。好歹你得给点东西证明一下你是谁吧。要不然不能过。 - 接下来就是权限控制了。先去请求缓存中获取请求,判断一下方法上是否含有权限注解(
Secured
),如果没有权限注解自然是直接放行。 - 如果使用了权限注解,获取操作类型,和资源类型,然后比较权限,符合就服务,不符合就抛异常,具体实现在
AuthManager
(授权管理)中实现。 - 如果
AuthManager
(授权管理) 权限校验没有通过就抛出AccessException
。 - 如果捕捉到
AccessException
,请求结束返回403
。
权限操作类型
如上所述:nacos只有读和写两种操作类型,定义如下 :
package com.alibaba.nacos.auth.common;
public enum ActionTypes {
/**
* Read.
*/
READ("r"),
/**
* Write.
*/
WRITE("w");
private String action;
}
控制器缓存
控制器缓存用来解决多次反射的问题,在Nacos启动时扫描所有控制器,将请求路径与控制器方法匹配。
内部实现了两个Map,map使用 ConcurrentHashMap
实现,保证了高并发的效率,有了该缓存也减少了反射的次数,提供了Nacos的性能。
内部两个Map定义如下:
// 请求与方法的对照表
private ConcurrentMap<RequestMappingInfo, Method> methods = new ConcurrentHashMap<>();
// 请求地址与请求方法的匹配集合
private final ConcurrentMap<String, List<RequestMappingInfo>> urlLookup = new ConcurrentHashMap<>();
methods
是请求与方法的对应表,一个请求对应一个方法。urlLookup
是地址与请求列表的对应表,key是地址,格式为POST-->com.alibaba.nacos.core.controller/nacos/v1/install
,正常情况下一个地址
对应一个请求,但是可能存在一个请求地址
对应多个请求
。
获取方法
在权限过滤器中,在执行注解逻辑中,首先需要根据请求,从缓存控制器中找到匹配的方法。
Method method = methodsCache.getMethod(req);
获取方法实现逻辑如下:
- 获取请求路径,组装出
urlLookup
关系表中的Key。 - 根据拼接出来的请求路径获取到匹配的请求列表。
- 匹配列表筛选出最佳匹配的请求对象
RequestMappingInfo
。 - 根据请求对象找到对应的
Method
,并且返回。
初始化两个对象表
初始化方法:
/**
* init初始化反射方法
*
* @param packageName package name
*/
public void initClassMethod(String packageName) {
Reflections reflections = new Reflections(packageName);
Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(RequestMapping.class);
for (Class clazz : classesList) {
initClassMethod(clazz);
}
}
反射Nacos使用了 reflections
技术,Maven依赖如下:
<!-- https://github.com/ronmamo/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
代码篇幅过长,已提交至Github,完整代码请见 (已经添加了注解):https://github.com/keepgoon/nacos/blob/develop/core/src/main/java/com/alibaba/nacos/core/code/ControllerMethodsCache.java
Naco读后感
使用对象缓存,减少了反射次数,反射比较耗费性能,这样做大大提高了性能,这种方式效率是高于Sping AOP的,因为并不是每次都需要做反射。
ConcurrentHashMap
保证了多线程的效率。分段锁要比对象锁效率要高。
使用别人造好的轮子
reflections
,减少了反射的代码量和使用难度。
SRP
(单一职责原则),单一职责原则不但可以降低复杂度,帮助编程人员理清逻辑,还可以很大程度提高代码可读性,既见既所得。
以上是关于nacos源码之Auth(权限)模块-1(授权过滤器与控制器缓存)的主要内容,如果未能解决你的问题,请参考以下文章
nacos源码之Auth(权限)模块-2(权限管理与权限配置)