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配置中增加判断是否为不可见值才来更新数据

原文地址:https://www.cnblogs.com/WongHugh/p/12259098.html