SpringCloud微服务实现数据权限控制

发布网友 发布时间:2小时前

我来回答

1个回答

热心网友 时间:6分钟前

前章讲了如何进行用户权限验证,它是微服务下统一资源访问权限的控制,就像一道墙保护着SpringCloud集群下的各个业务应用服务。而本章要讲的是权限控制的另一个层面数据权限,意思是控制可访问数据资源的数量。

举个例子:

有一批业务员跟进全国的销售订单。他们被按城市进行划分,一个业务员跟进3个城市的订单,为了保护公司的业务数据不能被所有人都掌握,故每个业务员只能看到自己负责城市的订单数据。所以从系统来讲每个业务员都有访问销售订单的功能,然后再需要配置每个业务员负责的城市,以此对订单数据进行筛选。

要实现此功能有很多方法,如果系统中多个地方都需要类似的需求,那我们就可以将其提出来做成一个通用的功能。这里我介绍一个相对简单的解决方案,以供参考。

一、整体架构

数据权限为作一个注解的形式挂在每一个需要数据权限控制的Controller上,由于和具体的程序逻辑有关故有一定的入侵性,且需要数据库配合使用。

二、实现流程

浏览器传带查询权限范围参数访问Controller,如cities

POSThttp://127.0.0.1:8000/order/queryaccept:*/*Content-Type:application/jsontoken:1e2b2298-8274-4599-a26f-a799167cc82f{"cities":["cq","cd","bj"],"userName":"string"}

通过注解拦截权限范围参数,并根据预授权范围比较,回写在授权范围内的权限范围参数

cities=["cq","cd"]

通过参数传递到DAO层,在SQL语句中拼装出查询条件,实现数据的过滤

select*fromorderwherecityin('cq','cd')三、实现步骤1.注解实现

注解的完整代码,请详见源代码

1)创建注解@Retention(value=RetentionPolicy.RUNTIME)@Target(value={ElementType.METHOD})@Documentedpublic@interfaceScopeAuth{Stringtoken()default"AUTH_TOKEN";Stringscope()default"";String[]scopes()default{};}

此注解为运行时RetentionPolicy.RUNTIME作用在方法上ElementType.METHOD的

token:获取识别唯一用户的标识,与用户数据权限存储有关

scope,scopes:预请求的数据权限范围

2)AOP实现注解publicclassScopeAuthAdvice{@Around("@annotation(scopeAuth)")publicObjectbefore(ProceedingJoinPointthisJoinPoint,ScopeAuthscopeAuth)throwsThrowable{//...省略过程//获取tokenStringauthToken=getToken(args,scopeAuth.token(),methodSignature.getMethod());//回写范围参数setScope(scopeAuth.scope(),methodSignature,args,authToken);returnthisJoinPoint.proceed();}/***设置范围*/privatevoidsetScope(Stringscope,MethodSignaturemethodSignature,Object[]args,StringauthToken){//获取请求范围Set<String>requestScope=getRequestScope(args,scope,methodSignature.getMethod());ScopeAuthAdapteradapter=newScopeAuthAdapter(supplier);//已授权范围Set<String>authorizedScope=adapter.identifyPermissionScope(authToken,requestScope);//回写新范围setRequestScope(args,scope,authorizedScope,methodSignature.getMethod());}/***回写请求范围*/privatevoidsetRequestScope(Object[]args,StringscopeName,Collection<String>scopeValues,Methodmethod){//解析SPEL表达式if(scopeName.indexOf(SPEL_FLAG)==0){ParseSPEL.setMethodValue(scopeName,scopeValues,method,args);}}}

此为演示代码省略了过程,主要功能为通过token拿到预先授权的数据范围,再与本次请求的范围做交集,最后回写回原参数。

过程中用到了较多的SPEL表达式,用于计算表达式结果,具体请参考ParseSPEL文件

3)权限范围交集计算publicclassScopeAuthAdapter{privatefinalAuthQuerySuppliersupplier;publicScopeAuthAdapter(AuthQuerySuppliersupplier){this.supplier=supplier;}/***验证权限范围*@paramtoken*@paramrequestScope*@return*/publicSet<String>identifyPermissionScope(Stringtoken,Set<String>requestScope){Set<String>authorizeScope=supplier.queryScope(token);StringALL_SCOPE="AUTH_ALL";StringUSER_ALL="USER_ALL";if(authorizeScope==null){returnnull;}if(authorizeScope.contains(ALL_SCOPE)){//如果是全开放则返回请求范围returnrequestScope;}if(requestScope==null){returnnull;}if(requestScope.contains(USER_ALL)){//所有授权的范围returnauthorizeScope;}//移除不同的元素requestScope.retainAll(authorizeScope);returnrequestScope;}}

此处为了方便设置,有两个关键字范围

AUTH_ALL:预设所有范围,全开放的意思,为数据库预先设置值,请求传什么值都通过

USER_ALL:请求所有授权的范围,请求时传此值则会以数据库预设值为准

4)spring.factories自动导入类配置org.springframework.boot.autoconfigure.AutoConfigurationImportSelector=\fun.barryhome.cloud.annotation.ScopeAuthAdvice

如果注解功能是单独项目存在,在使用时有可能会存在找不到引入文件的问题,可通过此配置文件自动载入需要初始化的类

2.注解使用@ScopeAuth(scopes={"#orderDTO.cities"},token="#request.getHeader(\"X-User-Name\")")@PostMapping(value="/query")publicStringquery(@RequestBodyOrderDTOorderDTO,HttpServletRequestrequest){returnArrays.toString(orderDTO.getCities());}

在需要使用数据权限的controller方法上增加@ScopeAuth注解

scopes={"#orderDTO.cities"}:表示取输入参数orderDTO的cities值,这里是表达式必须加**#**

实际开发过程中,需要将**orderDTO.getCities()**带入后续逻辑中,在DAO层将此拼装在SQL中,以实现数据过滤功能

3.实现AuthStoreSupplier

AuthStoreSupplier接口为数据权限的存储接口,与AuthQuerySupplier配合使用,可按实际情况实现

此接口为非必要接口,可由数据库或Redis存储(推荐),一般在登录的同时保存在Redis中

4.实现AuthQuerySupplier

AuthQuerySupplier接口为数据权限查询接口,可按存储方法进行查询,推荐使用Redis

@ComponentpublicclassRedisAuthQuerySupplierimplementsAuthQuerySupplier{@AutowiredprivateRedisTemplate<String,String>redisTemplate;/***查询范围*/@OverridepublicSet<String>queryScope(Stringkey){StringAUTH_USER_KEY="auth:logic:user:%s";StringredisKey=String.format(AUTH_USER_KEY,key);List<String>range=redisTemplate.opsForList().range(redisKey,0,-1);if(range!=null){returnnewHashSet<>(range);}else{returnnull;}}}

在分布式结构里,也可将此实现提出到权限模块,采用远程调用方式,进一步解耦

5.开启数据权限@EnableScopeAuth@EnableDiscoveryClient@SpringBootApplicationpublicclassOrderApplication{publicstaticvoidmain(String[]args){SpringApplication.run(OrderApplication.class,args);}}四、综述

至此数据权限功能就实现了。在微服务器架构中为了实现功能的复用,将注解的创建和AuthQuerySupplier的实现提取到公共模块中,那么在具体的使用模块就简单得多了。只需增加@ScopeAuth注解,配置好查询方法就可以使用。

五、源代码

文中代码由于篇幅原因有一定省略并不是完整逻辑,如有兴趣请Fork源代码gitee.com/hypier/barry-cloud/tree/master/cloud-auth-logic

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com