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

 更新时间:2021年11月24日 17:14:25   作者:barry的异想世界  
这篇文章主要介绍的是权限控制的数据权限层面,意思是控制可访问数据资源的数量,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

举个例子:

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

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

一、 整体架构

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

二、 实现流程

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

POST http://127.0.0.1:8000/order/query
accept: */*
Content-Type: application/json
token: 1e2b2298-8274-4599-a26f-a799167cc82f

{"cities":["cq","cd","bj"],"userName":"string"}

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

cities = ["cq","cd"]

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

select * from order where city in ('cq','cd')

三、 实现步骤

1. 注解实现

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

1)创建注解

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
@Documented
public @interface ScopeAuth {

    String token() default "AUTH_TOKEN";
    String scope() default "";
    String[] scopes() default {};
}

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

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

scopescopes:预请求的数据权限范围

2) AOP实现注解

public class ScopeAuthAdvice {
  
    @Around("@annotation(scopeAuth)")
    public Object before(ProceedingJoinPoint thisJoinPoint, ScopeAuth scopeAuth) throws Throwable {
        // ... 省略过程
        // 获取token
        String authToken = getToken(args, scopeAuth.token(), methodSignature.getMethod());
            // 回写范围参数
        setScope(scopeAuth.scope(), methodSignature, args, authToken);
        
        return thisJoinPoint.proceed();
    }

    /**
     * 设置范围
     */
    private void setScope(String scope, MethodSignature methodSignature, Object[] args, String authToken) {
        // 获取请求范围
        Set<String> requestScope = getRequestScope(args, scope, methodSignature.getMethod());
        ScopeAuthAdapter adapter = new ScopeAuthAdapter(supplier);
        // 已授权范围
        Set<String> authorizedScope = adapter.identifyPermissionScope(authToken, requestScope);
        // 回写新范围
        setRequestScope(args, scope, authorizedScope, methodSignature.getMethod());
    }

    /**
     * 回写请求范围
     */
    private void setRequestScope(Object[] args, String scopeName, Collection<String> scopeValues, Method method) {
        // 解析 SPEL 表达式
        if (scopeName.indexOf(SPEL_FLAG) == 0) {
            ParseSPEL.setMethodValue(scopeName, scopeValues, method, args);
        }
    }
}

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

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

3)权限范围交集计算

public class ScopeAuthAdapter {

    private final AuthQuerySupplier supplier;

    public ScopeAuthAdapter(AuthQuerySupplier supplier) {
        this.supplier = supplier;
    }

    /**
     * 验证权限范围
     * @param token
     * @param requestScope
     * @return
     */
    public Set<String> identifyPermissionScope(String token, Set<String> requestScope) {
        Set<String> authorizeScope = supplier.queryScope(token);

        String ALL_SCOPE = "AUTH_ALL";
        String USER_ALL = "USER_ALL";

        if (authorizeScope == null) {
            return null;
        }

        if (authorizeScope.contains(ALL_SCOPE)) {
            // 如果是全开放则返回请求范围
            return requestScope;
        }

        if (requestScope == null) {
            return null;
        }

        if (requestScope.contains(USER_ALL)){
            // 所有授权的范围
            return authorizeScope;
        }

        // 移除不同的元素
        requestScope.retainAll(authorizeScope);

        return requestScope;
    }
}

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

  • 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")
public String query(@RequestBody OrderDTO orderDTO, HttpServletRequest request) {
    return Arrays.toString(orderDTO.getCities());
}

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

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

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

3. 实现AuthStoreSupplier

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

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

4. 实现AuthQuerySupplier

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

@Component
public class RedisAuthQuerySupplier implements AuthQuerySupplier {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 查询范围
     */
    @Override
    public Set<String> queryScope(String key) {
        String AUTH_USER_KEY = "auth:logic:user:%s";
        String redisKey = String.format(AUTH_USER_KEY, key);

        List<String> range = redisTemplate.opsForList().range(redisKey, 0, -1);

        if (range != null) {
            return new HashSet<>(range);
        } else {
            return null;
        }
    }
}

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

5. 开启数据权限

@EnableScopeAuth
@EnableDiscoveryClient
@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

四、 综述

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

五、源代码

文中代码由于篇幅原因有一定省略并不是完整逻辑,如有兴趣请Fork源代码

到此这篇关于SpringCloud 微服务数据权限控制的实现的文章就介绍到这了,更多相关SpringCloud内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java日期格式化如何避免YYYY引发的时间异常

    Java日期格式化如何避免YYYY引发的时间异常

    在编程中,日期格式化是一个常见的任务,使用不同的格式化选项可能会导致一些意外的结果,下面我们就来学习一下Java如何避免YYYY引发的时间异常吧
    2023-11-11
  • Java链表数据结构及其简单使用方法解析

    Java链表数据结构及其简单使用方法解析

    这篇文章主要介绍了Java链表数据结构及其简单使用方法解析,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07
  • Swagger文档自动生成PDF/HTML/Word解决方案详细指南

    Swagger文档自动生成PDF/HTML/Word解决方案详细指南

    本指南详细介绍了Swagger YAML/JSON定义、Swagger UI的使用、Swagger转为Markdown格式以及Markdown转为PDF/HTML/Word的过程,包括工具如Pandoc的运用,并提供了一个Spring Boot应用集成Swagger2和Swagger2Markup的示例,阐述了API文档的生成流程,感兴趣的朋友一起看看吧
    2025-08-08
  • Java图形化编程之JFrame疫苗接种系统详解

    Java图形化编程之JFrame疫苗接种系统详解

    GUI图形界面设计是用户和程序交互的工具,用户通过图形界面控制程序事件的发生。首先介绍Swing的基本体系结构,这是底层
    2021-09-09
  • springboot学习之Thymeleaf模板引擎及原理介绍

    springboot学习之Thymeleaf模板引擎及原理介绍

    本文主要介绍一下SpringBoot给我们推荐的Thymeleaf模板引擎,这模板引擎呢,是一个高级语言的模板引擎,他的这个语法更简单而且功能更强大,对springboot Thymeleaf模板引擎相关知识感兴趣的朋友一起看看吧
    2022-02-02
  • 启用设置org.slf4j.Logger打印并输出日志方式

    启用设置org.slf4j.Logger打印并输出日志方式

    这篇文章主要介绍了启用设置org.slf4j.Logger打印并输出日志方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Mybatis框架中Interceptor接口的使用说明

    Mybatis框架中Interceptor接口的使用说明

    这篇文章主要介绍了Mybatis框架中Interceptor接口的使用说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java使用Spring JdbcTemplate向in语句中传递参数的教程详解

    Java使用Spring JdbcTemplate向in语句中传递参数的教程详解

    这篇文章主要给大家介绍Java如何使用Spring JdbcTemplate向in语句中传递参数,文中有详细的流程步骤和代码示例,需要的朋友可以参考下
    2023-07-07
  • java学生信息管理系统设计

    java学生信息管理系统设计

    这篇文章主要为大家详细介绍了java学生信息管理系统设计,学生信息添加进入数据库的事务,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • 深入理解Java中的String(示例详解)

    深入理解Java中的String(示例详解)

    文章详细介绍了Java中String类的特点、用途、主要方法以及常见用法,String类是不可变的,具有字符串常量池,特定的内存结构,并随JDK版本更新而优化,它广泛用于表示和处理文本数据,并在内存管理和性能优化方面表现出色,感兴趣的朋友一起看看吧
    2025-03-03

最新评论