(转)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

 

 

与权限相关表结构SQLMySQL):

/********************************************************************/

/**

 * User, Group, Permission

 *

 * User-Group, User-Permission

 *

 * Group-Permission

 */

/**

 * 用户信息表

 */

drop table if exists sty_site_user;

create table sty_site_user (

id bigint primary key auto_increment,

name varchar(20) default '',

passwd varchar(64) default '',

email varchar(50) default '',

address varchar(100) default '',

memo varchar(200) default '',

gmt_create datetime,

gmt_update datetime

) type=INNODB, charset=GBK;

 

/**

 * 组权限信息表

 */

drop table if exists sty_user_group;

create table sty_user_group (

id bigint primary key auto_increment,

name varchar(100) default '',

memo varchar(200) default '',

gmt_create datetime,

gmt_update datetime

) type=INNODB, charset=GBK;

 

/**

 * 单个权限信息表

 */

drop table if exists sty_user_perm;

create table sty_user_perm (

id bigint primary key auto_increment,

name varchar(100) default '',

memo varchar(200) default '',

gmt_create datetime,

gmt_update datetime

) type=INNODB, charset=GBK;

 

/**

 * 用户与组权限关联表

 */

drop table if exists sty_usr_grp_cup;

create table sty_usr_grp_cup (

id bigint primary key auto_increment,

usr_id bigint default '0',

grp_id bigint default '0'

) type=INNODB, charset=GBK;

 

/**

 * 用户与权限关联表

 */

drop table if exists sty_usr_prm_cup;

create table sty_usr_prm_cup (

id bigint primary key auto_increment,

usr_id bigint default '0',

prm_id bigint default '0'

) type=INNODB, charset=GBK;

 

/**

 * 组权限和单个权限关联表

 */

drop table if exists sty_grp_prm_cup;

create table sty_grp_prm_cup (

id bigint primary key auto_increment,

grp_id bigint default '0',

prm_id bigint default '0'

) type=INNODB, charset=GBK;

 

posted @ 2017-09-20 15:49  LatteYan  阅读(84)  评论(0编辑  收藏  举报