`
obullxl
  • 浏览: 182044 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

通过Java注解实现安全控制

    博客分类:
  • Java
阅读更多

 想学习下SpringSecurity,看了下用户指南文档,觉得SpringSecurity的配置太多,并且有点复杂。一般在权限控制中,对资源访问的权限一般分为组权限(也有称角色,它包含多个单个的权限)和单个权限,那么我们完全可以在访问资源时,通过申明(Java注解)该资源所需的权限就可以达到目的了。


Java注解其实一直伴随着我们,在Java类中,我们经常会看到“@Override”、“@SuppressWarnings”等字符串,它们就是Java注解。就Java注解本身而言,它是不会对所注解的目标(类型,属性,方法,参数,构造函数,局部变量,注解和包)产生任何影响的,但它可配合其它工具(比如Eclipse,加上@SuppressWarnings(“unckecked”)后,那条黄色的警告线就消失了)或是程序(比如对属性加上@Autowire,则Spring就帮我们注入了)对注解目标产生作用。


一、定义注解类
@Target( { ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SecurityControl {
    public String[] role() default "";
    public boolean roleOr() default true;
    public String[] perm() default "";
    public boolean permOr() default false;
}
说明:Java代码去除了文件说明、版权、注释等内容。
 注解“SecurityControl”它有三个注解来说明本注解:
 Target:指示了SecurityControl可以注解的目标(类型,属性,方法,参数,构造函数,局部变量,注解和包),本注解只有类型和方法;
 Retention:指示了SecurityControl注解所适用的范围(源代码?类文件?运行期),因为要在VM运行的时候读取注解,所以本注解是运行期;
 Documented:指示了是加把本注解加到生成的Java文档中(JavaDoc),本注解加入。
 注解“SecurityControl”有四个属性:
 role[]:资源的组权限;
 roleOr:组权限是不是或关系,即只要满足其中之一即可,默认为是;
 perm[]:单个权限组;
 permOr:单个权限是不是或关系,默认为否,即全部的单个权限必须全部满足才行。

 

 二、权限异常类
 当权限不满足时,抛出异常,这样系统可以捕捉该异常,然后做相应的处理。
public class SecurityControlException extends RuntimeException {
    private static final long serialVersionUID = -8065906692358500801L;
   
    public SecurityControlException() {
    }
   
    public SecurityControlException(String errMsg) {
        super(errMsg);
    }
}
 该异常可以带一个异常消息,比如可以说明在进行什么操作时抛出该异常。

 

 三、权限持有类
public class SecurityControlHolder {
    private static final ThreadLocal<Set<String>> roles = new ThreadLocal<Set<String>>();
    private static final ThreadLocal<Set<String>> perms = new ThreadLocal<Set<String>>();
   
    public static void set(Set<String> rs, Set<String> ps) {
        roles.set(rs);
        perms.set(ps);
    }
   
    public static Set<String> getRoles() {
        return roles.get();
    }
   
    public static Set<String> getPerms() {
        return perms.get();
    }
   
    public static void clear() {
        roles.set(null);
        perms.set(null);
    }
   
    public static void checkPermission(SecurityControl sc) {
        if (checkRoles(sc) && checkPerms(sc)) {
            return;
        }
       
        throw new SecurityControlException("访问操作拒绝.");
    }
   
    private static boolean checkRoles(SecurityControl sc) {
        if (sc == null) {
            return true;
        }
       
        String[] roles = sc.role();
        if (roles == null || roles.length == 0) {
            return true;
        }
        List<String> list = new ArrayList<String>();
        for (String role : roles) {
            if (role != null && role.trim().length() > 0) {
                list.add(role.trim());
            }
        }
        if (list.isEmpty()) {
            return true;
        }
       
        Set<String> rs = getRoles();
        if (sc.roleOr()) {
            for (String role : roles) {
                if (rs.contains(role)) {
                    return true;
                }
            }
        } else {
            for (String role : roles) {
                if (!rs.contains(role)) {
                    return false;
                }
            }
            return true;
        }
       
        return false;
    }
   
    private static boolean checkPerms(SecurityControl sc) {
        if (sc == null) {
            return true;
        }
       
        String[] perms = sc.perm();
        if (perms == null || perms.length == 0) {
            return true;
        }
        List<String> list = new ArrayList<String>();
        for (String perm : perms) {
            if (perm != null && perm.trim().length() > 0) {
                list.add(perm.trim());
            }
        }
        if (list.isEmpty()) {
            return true;
        }
       
        Set<String> ps = getPerms();
        if (sc.permOr()) {
            for (String perm : perms) {
                if (ps.contains(perm)) {
                    return true;
                }
            }
        } else {
            for (String perm : perms) {
                if (!ps.contains(perm)) {
                    return false;
                }
            }
            return true;
        }
       
        return false;
    }
}
 每次验证权限“checkPermission(SecurityControl sc)”都是先验证组“checkRoles(SecurityControl sc)”,在组权限通过的情况下,再验证单个权限“checkPerms(SecurityControl sc)”,只有在组和单个权限都通过的情况下,才有权限访问资源,否则抛出权限不足异常(SecurityControlException)。

 

 四、访问切面类
 上面三步已经把准备工作做好了:我们可以通过注解来标示资源的权限,通过捕捉异常来决定当权限不足时做什么,可以通过一个简单的持有类来保持访问者的权限。现在还剩下最后一项工作:这个注解如何工作?可以通过拦截方法来达到该目的。
public class SecurityControlInterceptor implements MethodInterceptor {
    private static final Logger logger = Logger.getLogger(SecurityControlInterceptor.class);
   
  
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Class<?> clazz = method.getDeclaringClass();
       
        if (logger.isInfoEnabled()) {
            logger.info("Start Monitor: " + this.dumpInvocation(clazz, method, invocation.getArguments()));
        }
       
        SecurityControl sc = clazz.getAnnotation(SecurityControl.class);
        if (logger.isInfoEnabled()) {
            logger.info("Class SecurityControl: " + this.dumpSecurityControl(sc));
        }
       
        // 验证类安全
        SecurityControlHolder.checkPermission(sc);
       
        sc = method.getAnnotation(SecurityControl.class);
        if (logger.isInfoEnabled()) {
            logger.info("Method SecurityControl: " + this.dumpSecurityControl(sc));
        }
       
        // 验证方法安全
        SecurityControlHolder.checkPermission(sc);
       
        return invocation.proceed();
    }
   
    private String dumpInvocation(Class<?> clazz, Method method, Object[] args) {
        StringBuilder txt = new StringBuilder();
        txt.append("[Class: ").append(clazz.getSimpleName()).append("]");
        txt.append("[Method: ").append(method.getName()).append("]");
        txt.append("[Args: ").append(Arrays.toString(args)).append("]");
       
        return txt.toString();
    }
   
    private String dumpSecurityControl(SecurityControl sc) {
        if (sc != null) {
            return sc.toString();
        }
        return "null";
    }
}


 最后就是在Spring中配置拦截器。
 <bean id="serviceSecurityInterceptor" class="com.alipay.test.security.SecurityControlInterceptor" />
 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="interceptorNames">
   <list>
    <value>serviceSecurityInterceptor</value>
   </list>
  </property>
  <property name="beanNames">
   <value>*Service</value>
  </property>
 </bean>
 拦截器会拦截以Service结尾的类的方法。

 

 五、资源类(Service类)
@SecurityControl(role = { "ROLE_ADMIN", "ROLE_MANAGER" })
public interface SecurityService {
   
    public String getServiceName();
   
    @SecurityControl(perm = { "PERM_CREATE" })
    public void createService(String service);
   
    @SecurityControl(perm = { "PERM_READ" })
    public String getService();
}
 由注解可以看出:
1、 访问该类需要组权限:ROLE_ADMIN或是ROLE_MANAGER,只有该组权限满足后,才能进行类方法的调用;
2、访问方法createService()需要PERM_CREATE单个权限;
 3、访问方法getService()需要PERM_READ权限;
 4、访问方法getServiceName()除了类所需要的权限外,不需要额外权限。
 该接口的实现类:
@Component("securityService")
public class SecurityServiceImpl implements SecurityService {
   
    private String service = SecurityServiceImpl.class.getName();
   
    /**
     * @see com.aboy.SecurityService.test.annotation.service.AnnotationService#createService()
     */
    public void createService(String service) {
        this.service = service;
    }
   
    /**
     * @return
     * @see com.aboy.SecurityService.test.annotation.service.AnnotationService#getService()
     */
    public String getService() {
        return this.service;
    }
   
    /**
     * @return
     * @see com.aboy.SecurityService.test.annotation.service.AnnotationService#getServiceName()
     */
    public String getServiceName() {
        return "ServiceName";
    }
   
}

 

 六、测试
 @Test
public void testSecurityControl() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/spring/security-context.xml");
        AnnotationService service = ctx.getBean(SecurityService.class);
       
        Set<String> roles = new HashSet<String>();
        roles.add("ROLE_ADMIN");
       
        Set<String> perms = new HashSet<String>();
        perms.add("PERM_READ");
       
        SecurityControlHolder.set(roles, perms);
       
        logger.info(service.getService());
        logger.info(service.getServiceName());
        service.createService(AnnotationServiceTest.class.getName());
        logger.info(service.getService());
    }


 运行用例发现,不通过,原因里没有PERM_CREATE权限。

 

 到此,整个注解的学习与使用已经结束了。整个权限部分,只有四个主要的类,它们完成的功能相当的简单:拦截以Service结尾的类的方法调用,获取该类和调用的方法的权限,判断权限,如果权限验证通过,则程序继续往下走,否则,抛出异常。


参考资料
百度:http://www.baidu.com和谷歌:http://www.g.cn

0
2
分享到:
评论
1 楼 jiangjuliang 2012-04-19  
[b][/b][i][/i][u][/u]
引用

    [*]
[img][/img][url][/url][flash=200,200][/flash]
[align=left][/align][/size][size=medium][size=large][/size]ddd

相关推荐

    编程语言+JAVAspring+安全框架+权限控制

    编程语言+JAVAspring+安全框架+权限控制**...它介绍了JAVAspring的安全框架的概念、原理和作用,以及如何使用JAVAspring的安全框架来实现权限控制,包括认证、授权、加密、过滤等内容,以及一些配置文件和注解的用法。

    JAVA上百实例源码以及开源项目

     Java实现HTTP连接与浏览,Java源码下载,输入html文件地址或网址,显示页面和HTML源文件,一步步的实现过程请下载本实例的Java源码,代码中包括丰富的注释,对学习有帮助。 Java实现的FTP连接与数据浏览程序 1个...

    JAVA上百实例源码以及开源项目源代码

     Java实现HTTP连接与浏览,Java源码下载,输入html文件地址或网址,显示页面和HTML源文件,一步步的实现过程请下载本实例的Java源码,代码中包括丰富的注释,对学习有帮助。 Java实现的FTP连接与数据浏览程序 1个...

    java开源包4

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包3

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包11

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包6

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包9

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包101

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包5

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包8

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包10

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    JavaSE安装包(3) 包括scala-SDK,向日葵(远程工具),阿里Java代码规范等

    它能够帮助用户在不同设备之间建立远程连接,并实现远程控制和文件传输。向日葵具有简单易用、高效稳定的特点,适用于远程办公、远程教学和技术支持等场景。 3. 阿里Java代码规范:阿里Java代码规范是阿里巴巴公司...

    java开源包1

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    是一个低代码 全栈类 框架,它使用 Java 注解 动态生成页面以及增、删、改、查、权限控制等后台功能

    是一个低代码 全栈类 框架,它使用 Java 注解 动态生成页面以及增、删、改、查、权限控制等后台功能;对象视图模型 ,零前端代码、零代码生成、零SQL、零API声明、零DTO / VO / BO 创建,表结构注释自动生成 内置...

    Java毕业设计-java基于BS结构下的OA流程可视化的研究与实现(源代码+论文).rar

    4. 角色权限控制:根据用户角色分配不同的权限,确保系统的安全性。 项目具有良好的扩展性和可定制性,可以根据实际需求进行二次开发和定制。同时,项目还提供了详细的文档和注释,方便学生理解和掌握项目的核心...

Global site tag (gtag.js) - Google Analytics