• 2020-02-04 功能实现:根据用户角色对数据进行不可见处理


    功能概述

    不同用户对同一个数据列表或者单条数据存在不同的查看权限,可能A角色可以看到col1、col2,而B角色可以看到col1、col3;因此需要通过配置角色的权限,能更改用户的查看权限,当无权限查看时,该字段属性值为*

    框架说明

    前后端分离架构,后端使用Spring @RequestBody响应数据,定义了一个ResultBean对象进行统一响应数据

    实现思路

    统一拦截响应结果,对具有自定义注解的请求,根据权限对列进行不可见处理;配置用到的数据直接通过扫描注解取得

    实现细节

    1. Spring通过ApplicationContext扫描配置的数据,只能扫描到类级别的注解,因此需要定义一个类注解,获取需要识别的Bean

    1 @Target({ElementType.TYPE})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface ShowColsScan {
    4 }

    以上注解是为了进行配置时,能正确扫描到配置项所在的类,获取配置列表

    2. 扫描到包含配置项的Bean后,需要定义注解用于实际需要进行处理不可见的请求

    1 @Target({ElementType.METHOD})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface ShowCols {
    4     String id(); // 唯一的处理ID
    5     String desc(); // 方便查看用的描述
    6     Col[] cols(); // 供配置选择的列
    7 }
    1 public @interface Col {
    2     String name(); // 列属性链
    3     String desc(); // 属性名
    4     boolean require() default false; // 是否必须
    5 }

    以上注解是为了进行配置时,有哪些请求需要进行数据不可见处理,分别含有哪些列可供配置

    3. 编码获取配置项的代码,因为每次部署项目后,配置项是固定的,如果每次都需要重新扫描一次,性能太差,所以进行缓存

     1 public class ShowColsConfigService implements ApplicationContextAware {
     2     private ApplicationContext applicationContext;
     3     private static List<ShowColsConfigAnnotation> showColsConfigs = new ArrayList<>();
     4 
     5     @Override
     6     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
     7         this.applicationContext = applicationContext;
     8     }
     9 
    10     synchronized public List<ShowColsConfigAnnotation> getShowColsConfigAnnotation(){
    11         if(showColsConfigs.size()>0){
    12             return showColsConfigs;
    13         }
    14         Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(ShowColsScan.class);
    15         for (String key : beansWithAnnotation.keySet()) {
    16             //Spring 代理类导致Method无法获取,这里通过父类获取
    17             Method[] methods = beansWithAnnotation.get(key).getClass().getSuperclass().getMethods();
    18             for (Method method : methods) {
    19                 System.out.println(method.getName());
    20                 //获取指定方法上的注解的属性
    21                 ShowCols showCols = AnnotationUtils.findAnnotation(method, ShowCols.class);
    22                 if (null != showCols) {
    23                     ShowColsConfigAnnotation showColsConfig = new ShowColsConfigAnnotation(showCols.id(), showCols.desc());
    24                     for(Col col: showCols.cols()){
    25                         showColsConfig.addCols(new Column(col.name(),col.desc()));
    26                     }
    27                     showColsConfigs.add(showColsConfig);
    28                 }
    29             }
    30         }
    31         return showColsConfigs;
    32     }

    ShowColsConfigAnnotation、Column类与注解一样,只是多了构造方法;showColsConfigs存储的内容可以根据前端需要的数据获取返回需要的格式

    4. 通过ResponseBodyAdvice拦截响应结果

      1 /**
      2  * 结果拦截器:根据权限对结果集进行处理
      3  */
      4 @RestControllerAdvice
      5 public class MyResponseBodyAdvice implements ResponseBodyAdvice {
      6     @Autowired
      7     private ShowColsConfigService showColsConfigService;
      8 
      9     /**
     10      * 当包含注解@ShowColByPermission时才进行拦截
     11      */
     12     @Override
     13     public boolean supports(MethodParameter methodParameter, Class aClass) {
     14         return getShowColByPermissionAnnotation(methodParameter) != null;
     15     }
     16 
     17     /**
     18      * 处理返回结果:根据角色显示数据
     19      */
     20     @Override
     21     public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
     22         if(!(o instanceof ResultBean)){
     23             return o;
     24         }
     25 
     26         // 1. 获得注解信息
     27         ShowCols annotation = getShowColByPermissionAnnotation(methodParameter);
     28         if(annotation == null){
     29             return o;
     30         }
     31 
     32         // 2. 获得需要屏蔽的列:注解中配置的列-配置的列-必须的列
     33         Set<String> allCols = new HashSet<>();
     34         Set<String> showCols = new HashSet<>();
     35         // 2.1 分别得到所有列allCols及必须列showCols
     36         for(Col col :annotation.cols()){
     37             allCols.add(col.name());
     38             if(col.require()){
     39                 showCols.add(col.name());
     40             }
     41         }
     42         // 2.2 获得配置了的角色的列
     43         for(Role role : UserUtils.getRoleList()){
     44             Optional.ofNullable(showColsConfigService.get(new ShowColsConfigPo(annotation.id(),role.getId())))
     45                 .map(ShowColsConfigPo::getShowColPaths)
     46                 .ifPresent(obj->showCols.addAll(Arrays.asList(obj.split(","))));
     47         }
     48         // 2.3 做减法得到屏蔽的列
     49         allCols.removeAll(showCols);
     50 
     51         // 3. 根据屏蔽列处理数据
     52         List list = null;
     53         Object result = ((ResultBean) o).getResult();
     54         if(result instanceof Page) {
     55             list = ((Page) result).getList();
     56         }else if(result instanceof List){
     57             list = (List)result;
     58         }else{
     59             list = Arrays.asList(result);
     60         }
     61 
     62         if(list.size()==0) {
     63             return o;
     64         }
     65 
     66         for(String col : allCols){
     67             writeExprByDefault("#{"+col+"}",list);
     68         }
     69 
     70         return o;
     71     }
     72 
     73     private static void writeExprByDefault(String expr, List<Object> data) {
     74         ExpressionParser parser = new SpelExpressionParser();
     75         Expression expression = parser.parseExpression(expr, new TemplateParserContext());
     76 
     77         Object value = null;
     78         String typeName = expression.getValueType(data.get(0)).getName();
     79         if ("java.util.Date".equals(typeName)) {
     80            value = null;
     81         } else if ("java.lang.String".equals(typeName)) {
     82            value = "*";
     83         } else if ("long".equals(typeName)) {
     84             value = 0l;
     85         } else if ("int".equals(typeName)) {
     86             value = 0;
     87         }
     88 
     89         for(Object obj : data){
     90             expression.setValue(obj, value);
     91         }
     92     }
     93 
     94     /**
     95      * 获得注解@ShowCols对象
     96      */
     97     private ShowCols getShowColByPermissionAnnotation(MethodParameter methodParameter){
     98         if(methodParameter.getExecutable().getDeclaringClass().getAnnotation(ShowColsScan.class)==null){
     99             return null;
    100         }
    101         return methodParameter.getMethod().getDeclaredAnnotation(ShowCols.class);
    102     }
    103 }

    获取扫描到的所有配置项,与数据库中的配置进行对比做减法,剩下的列进行不可见处理

    5. 附录配置表

     

    使用步骤

    1. 在需要控制列显示的请求所在的类上增加注解@ShowColsScan

    1 @ShowColsScan
    2 public class OrderProductPriceController {

    2. 在需要控制列显示的请求所在的方法上增加注解@ShowCols

     1 @ShowCols(id="42a672b243f911eaae330221860e9b7e",desc="电商报价单列表", cols={
     2             @Col(name="code",desc="代码"),
     3             @Col(name="obj.fObjName",desc="报价对象"),
     4             @Col(name="fcusname",desc="客户名称"),
     5             @Col(name="fonlinecode",desc="网店单号"),
     6             @Col(name="fstanderprize",desc="标准价格"),
     7             @Col(name="fdiscountprize",desc="折后价格"),
     8             @Col(name="fRemark",desc="备注"),
     9             @Col(name="fdate",desc="日期"),
    10             @Col(name="checkFlag",desc="审核",require = true)
    11 })
    12 public ResultBean list(OrderProductPrice baseMirrorMessage, HttpServletRequest request, HttpServletResponse response, String dsf) {

    3. 在功能页面上,配置角色与列显示对应关系

    备注:保存时数据前端数据被更改了,因此需要在SQL配置中增加判断是否为不可见值才来更新数据

  • 相关阅读:
    根据字符串当作变量,进行类名转换
    Python 字符分割时,只分割最后一个(rsplit的使用)
    Python之99乘法表代码
    linux 同时执行多个命令及几个基础命令
    什么是CLI、GUI
    linux命令-压缩数据
    Linux查看进程
    Linux排序数据
    Linux检测磁盘空间
    linux结束进程命令
  • 原文地址:https://www.cnblogs.com/WongHugh/p/12259098.html
Copyright © 2020-2023  润新知