权限管理系统

Posted 未来畅想--娜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了权限管理系统相关的知识,希望对你有一定的参考价值。

权限可以分为两大类:操作权限与数据权限。见此篇博文: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);

以上是关于权限管理系统的主要内容,如果未能解决你的问题,请参考以下文章

Linux组管理和权限管理

以编程方式创建文件夹以及使用 java 将内容保存到该位置的权限

怎么在百度地图上看我的位置

Linux组管理和权限管理

权限管理系统

本地化Info.plist权限Key