下一篇:《nacos源码之Auth(权限)模块-2(权限管理与权限配置)》
上一篇《nacos源码构建与总览》浏览器还挺多,接下来模块的精讲来了
《nacos源码构建与总览》
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的权限实现 :
servlet
,并不是netty的非阻塞框架。
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
属性,有的话才去交给另一个过滤器执行。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
使用对象缓存,减少了反射次数,反射比较耗费性能,这样做大大提高了性能,这种方式效率是高于Sping AOP的,因为并不是每次都需要做反射。
ConcurrentHashMap
保证了多线程的效率。分段锁要比对象锁效率要高。
使用别人造好的轮子
reflections
,减少了反射的代码量和使用难度。
SRP
(单一职责原则),单一职责原则不但可以降低复杂度,帮助编程人员理清逻辑,还可以很大程度提高代码可读性,既见既所得。
下一篇:《nacos源码之Auth(权限)模块-2(权限管理与权限配置)》