权限管理系统(七)

权限可以分为两大类:操作权限与数据权限。见此篇博文:http://blog.csdn.net/sharetop/article/details/50281669

Shiro帮我们实现的大多为操作权限,那么今天我想分享一个数据权限的方案,主要采用的仍是注解+切面拦截。

思路大概是这样的:

  1. 在controller的方法参数,约定包含一个Map类型的parameters
  2. 通过注解声明一下当前用户的某个成员属性值需要被插入到这个parameters中,并且声明对应的字段名称
  3. 在方法体中,就可以将parameters中所有成员拿出来生成SQL,实现数据的筛选。

比如,我们需要根据当前登录用户的名称realName,筛选出saleName为当前用户名称的销售数据,又或者,根据当前登录用户的groupNames[0]为北京,筛选出所有数据字段province为北京的计费数据。

首先,我们定义注解 RequiresData,代码如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresData {

    String[] props() default "";

    String[] fields() default "";

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

两个属性都是字符串数组,所以我们要使用时可以是这样的:

@RequiresData(props={"realName","groupNames[0]"},fields={"saleName","province"})
 
 
  • 1
  • 1

然后,我们需要修改AuthorizationAttributeSourceAdvisor,同样添加对新注解的支持。

private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
            new Class[] {
                    RequiresPermissions.class, RequiresRoles.class,
                    RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class,
                    RequiresAction.class,RequiresData.class
            };
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

也同样修改我们继承的AopAllianceAnnotationsAuthorizingMethodInterceptor,添加新的DataAnnotationMethodInterceptor支持。

public AopAllianceAnnotationsAuthorizingMethodInterceptor(){
        super();

        this.methodInterceptors.add(new ActionAnnotationMethodInterceptor(new SpringAnnotationResolver()));
        this.methodInterceptors.add(new DataAnnotationMethodInterceptor(new SpringAnnotationResolver()));

    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这次我们的DataAnnotationHandler是不需要做任何事情的,因为我们不是做权限验证,而是要修改方法参数。

所以,我们需要先定义一个接口。

public interface DataParameterRequest {
    Map<String,String> getParameters();
}
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

保证我们在方法参数实现此接口,比如我们的参数是ConditionRequest,那么代码如下:

public class ConditionRequest implements DataParameterRequest{

    public String author;

    private Map<String,String> params = new HashMap<String,String>();

    @Override
    public Map<String, String> getParameters() {
        // TODO Auto-generated method stub
        return params;
    }

    public void setParameters(Map<String,String> p){
        this.params=p;
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

然后在Controller的方法是这样的:

@RequiresData(props={"realName","groupNames[1]"},fields={"saleName","province"})
    @RequestMapping(value = "/data", method = RequestMethod.POST, headers = {
            "Content-Type=application/json;charset=utf-8", "Accept=application/json" })
    public @ResponseBody Map<String,String> showData(@RequestBody ConditionRequest req){
//这里需要根据req.getParameters()得到的Map去构造出SQL查询条件,筛选出数据
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

下面讨论一下如何利用AOP修改方法参数,主要是两个地方要修改,一是AopAllianceAnnotationsAuthorizingMethodInterceptor中需要重载invoke,让它能保证在遇到RequiresData时能调用DataAnnotationMethodInterceptor的invoke。

@Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
        assertAuthorized(mi);

        Collection<AuthorizingAnnotationMethodInterceptor> aamis = getMethodInterceptors();
        if (aamis != null && !aamis.isEmpty()) {
            for (AuthorizingAnnotationMethodInterceptor aami : aamis) {
                if (aami.supports(mi)){

                    //针对DataAnnotationMethodInterceptor,有特殊的处理
                    if(aami instanceof DataAnnotationMethodInterceptor) {
                        return ((DataAnnotationMethodInterceptor)aami).invoke(mi);
                    }

                } 
            }
        }

        //其它情况均使用系统缺省
        return super.invoke(mi);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

再者需要修改DataAnnotationMethodInterceptor,同样重载invoke方法,这是主要功能逻辑所在位置。

@SuppressWarnings("unchecked")
    private Map<String,String> _addParameters(String[] props,String[] fields,Class<?> clz,Object principal) throws Exception {
        Map<String,String> params = new HashMap<String,String>();

        for(int i=0;i<props.length;i++){
            String prop = props[i];
            String field = fields[i];
            int index = -1;

            String[] strs = StringUtils.tokenizeToStringArray(prop, "[]");
            if(strs.length>1){
                prop = strs[0];
                index = Integer.valueOf(strs[1]);
            }

            String propValue = "";
            Field p = clz.getDeclaredField(prop);
            if(Modifier.PRIVATE==p.getModifiers()){
                String m_getter_name = "get"+StringUtils.uppercaseFirstChar(prop);
                Method method = clz.getDeclaredMethod(m_getter_name);
                Object ret = method.invoke(principal);
                if(index>-1 && ret instanceof List<?>){
                    propValue = ((List<Object>)ret).get(index).toString();
                }
                else
                    propValue = ret.toString();
            }
            else{
                Object ret = p.get(principal);
                if(index>-1 && ret instanceof List<?>){
                    propValue = ((List<Object>)ret).get(index).toString();
                }
                else {
                    propValue = ret.toString();
                }
            }
            System.out.println(propValue);

            params.put(field, propValue);

        }
        return params;
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // TODO Auto-generated method stub
        assertAuthorized(methodInvocation);

        Object obj = methodInvocation.getThis();
        Object[] args = methodInvocation.getArguments();

        RequiresData an = (RequiresData)this.getAnnotation(methodInvocation);
        Object principal = this.getSubject().getPrincipal();
        Class<?> clz = principal.getClass();

        String[] props = an.props();
        String[] fields = an.fields();

        for(Object o : args){
            if( o instanceof DataParameterRequest ){
                Map<String,String> m = (Map<String,String>)((DataParameterRequest)o).getParameters();
                if(m!=null){
                    Map<String,String> mm = this._addParameters(props, fields, clz, principal);
                    m.putAll(mm);
                }
            }
        }

        return methodInvocation.getMethod().invoke(obj, args);

    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

大概解释一下,在invoke中,当前登录的用户是这个Object principal = this.getSubject().getPrincipal(); ,然后取出方法参数,是个数组,Object[] args = methodInvocation.getArguments(); 找到它里面那个DataParameterRequest类型的参数,根据注解声明的属性方法与Map中的字段对应关系,添加到args中的那个DataParameterRequest中的parameters里面去。就可以了。 
注意,最后需要将args传入methodInvocation.getMethod().invoke(obj, args);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值