本文共 4307 字,大约阅读时间需要 14 分钟。
下一篇:
Nacos的Auth模块
上一篇《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 parser() default DefaultResourceParser.class;}
nacos 权限模块只定义了两种操作类型,
读
和写
。但从这个角度也无法胜任业务系统的权限框架,但是对于服务发现,配置管理视乎也够用了。
在一个通用的权限框架中,权限框架只需要管理权限资源的操作类型是否通过即可,这一点Nacos是相同的。
注解实现讲解
这里就是整个权限模块的核心了。
该注解的实现并不在auth包下,但是注解的实现也是属于权限模块的,因此在本章节讲解。
看这个代码之前,先强调一些知识,帮助理解nacos的权限实现 :
- nacos使用spring mvc架构。因此它是
servlet
,并不是netty的非阻塞框架。 - nacos注解使用过滤器实现。
netty
与servlet
并没有优略之分,它们两个亦不是水火不容,使用netty
还是使用servlet
,需要根据解决问题的场景参考,它们本身就是为服务实现提出的不同的解决方案。
由于代码篇幅过长,带有备注的代码已经提交到 ,请见 :
核心片段:
// 获取缓存方法 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 ConcurrentMapmethods = new ConcurrentHashMap<>(); // 请求地址与请求方法的匹配集合 private final ConcurrentMap > 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> classesList = reflections.getTypesAnnotatedWith(RequestMapping.class); for (Class clazz : classesList) { initClassMethod(clazz); } }
反射Nacos使用了 reflections
技术,Maven依赖如下:
org.reflections reflections 0.9.12
代码篇幅过长,已提交至Github,完整代码请见 (已经添加了注解):
Naco读后感
使用对象缓存,减少了反射次数,反射比较耗费性能,这样做大大提高了性能,这种方式效率是高于Sping AOP的,因为并不是每次都需要做反射。
ConcurrentHashMap
保证了多线程的效率。分段锁要比对象锁效率要高。
使用别人造好的轮子
reflections
,减少了反射的代码量和使用难度。
SRP
(单一职责原则),单一职责原则不但可以降低复杂度,帮助编程人员理清逻辑,还可以很大程度提高代码可读性,既见既所得。
下一篇:
转载地址:https://keepgoon.blog.csdn.net/article/details/116752891 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!