Java后台框架篇--Spring Security保护bean方法

来源:互联网 发布:abb离线编程软件 编辑:程序博客网 时间:2024/05/24 04:30

在本章中我们将会看到如何使用Spring Security保护bean方法。

1.使用注解保护方法

在Spring Security中实现方法级安全性的最常见办法是使用特定的注解,将这些注解应用到需要保护的方法上。这样有几个好处,最重要的是当我们在编辑器中查看给定的方法时,能够很清楚地看到它的安全规则。 
Spring Security提供了三种不同的安全注解:

  • Spring Security自带的@Security注解
  • JSR-250的@RolesAllowed注解
  • 表达式驱动的注解,包括@PreAuthorize,@PostAuthorize,@PreFilter,@PostFilter。

1.1使用@Secured注解限制方法调用

在Spring中,如果要启用基于注解的方法安全性,关键之处在于要在配置类上使用@EnableGlobalMethodSecurity:

@Configuration@EnableGlobalMethodSecurity(securedEnabled=true)public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{}
  • 1
  • 2
  • 3
  • 4
  • 5

GlobalMethodSecurityConfiguration类能够为方法级别的安全性提供更精细的配置。 
如果securedEnabled属性的值为true的话,将会创建一个切点,这样的话Spring Security切面就会包装带有@Security注解的方法。(所以最好把要控制权限的方法放在service层)。

示例:

@Servicepublic class SecurityService {    @Secured("ROLE_ADMIN")    public void test1(){        System.out.println("拥有ADMIN权限");    }    @Secured({"ROLE_ADMIN","ROLE_USER"})    public void test2(){        System.out.println("拥有ADMIN或USER权限");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

@Secured注解的不足之处在于它是spring特定的注解。如果更倾向于使用Java标准定义的注解,那么你应该考虑使用@RolesAllowed注解

1.2在Spring Security中使用JSR-250的@RolesAllowed注解

@RolesAllowed是JSR-250定义的Java标准注解。如果选择@RolesAllowed的话,需要将@EnableGlobalMethodSecurity的jsr250Enabled属性设置为true,以开启此功能:

@Configuration@EnableGlobalMethodSecurity(securedEnabled=true,jsr250Enabled=true)public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{}
  • 1
  • 2
  • 3
  • 4
  • 5

使用示例:

@Servicepublic class SecurityService {    @RolesAllowed("ROLE_ADMIN")    public void test1(){        System.out.println("拥有ADMIN权限");    }    @RolesAllowed({"ROLE_ADMIN","ROLE_USER"})    public void test2(){        System.out.println("拥有ADMIN或USER权限");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.使用表达式实现方法级别的安全性

Spring Security 3.0提供了4个新的注解,可以使用SpEL表达式来保护方法调用

注解描述@PreAuthorize在方法调用之前,基于表达式的计算结果来限制对方法的访问@PostAuthorize允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常@PostFilter允许方法调用,但必须按照表达式来过滤方法的结果@PreFilter允许方法调用,但必须在进入方法之前过滤输入值

首先,我们需要将@EnableGlobalMethodSecurity的prePostEnabled属性值设为true,从而开启它们:

@Configuration@EnableGlobalMethodSecurity(securedEnabled=true,jsr250Enabled=true,prePostEnabled=true)public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{}
  • 1
  • 2
  • 3
  • 4
  • 5

2.1表述方法访问规则

@PreAuthorize和@PostAuthorize能够基于表达式的计算结果来限制方法的访问。区别在于表达式执行的时机。@PreAuthorize的表达式会在方法调用之前执行,如果表达式的计算结果不为true的话,将会阻止方法执行。与之相反,@PostAuthorize的表达式知道方法返回才会执行,然后根据结果决定是否抛出安全性异常,如果为false,会抛出一个AccessDeniedException异常,而调用者也得不到返回的值。

@Servicepublic class SecurityService {    @PreAuthorize("hasRole('ROLE_ADMIN') and #num > 5")    public void test1(int num){        System.out.println("拥有ADMIN权限并且输入的数字大于5");    }    @PostAuthorize("returnObject == 'abc'")    public String test2(){        System.out.println("执行方法并返回一个字符串对象");        return "abcd";    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

为了便利地访问受保护方法的返回对象,Spring Security在SpEL中提供了名为returnObject的变量。

2.2 过滤方法的输入和输出

@PostFilter和@PreFilter都使用一个SpEL作为值参数,但是这个表达式不是用来限制方法访问的,@PostFilter会使用这个表达式计算该方法所返回集合的每个成员,将计算结果为false的成员从结果集中除掉,返回结果集。而@PreFilter过滤的是要进入方法中的集合,同样会对每一条数据进行检查,只有满足表达式(即表达式返回true)的结果才能保留在集中,最终传到方法里面。

@Servicepublic class SecurityService {    /**     * 需要拥有ADMIN或USER权限才能访问,     * ADMIN缺陷的话返回所有数据,     * USER权限的话只返回和登录用户相同用户名的数据     * @return     */    @PreAuthorize("hasAnyRole({'ROLE_ADMIN','ROLE_USER'})")    @PostFilter("hasRole('ROLE_ADMIN') || "            + "filterObject.username == principal.username")    public List<User> test1(){        User u1 = new User("xuexiaoqiang");        User u2 = new User("admin");        User u3 = new User("xueqiang");        List<User> list = new ArrayList<User>();        list.add(u1);        list.add(u2);        list.add(u3);        return list;    }    /**     * 需要拥有ADMIN或USER权限才能访问,     * 拥有ADMIN权限的用户会将数据全部传入方法中     * 拥有USER权限的用户只会将相同用户名的数据传进去     * @param users     */    @PreAuthorize("hasAnyRole({'ROLE_ADMIN','ROLE_USER'})")    @PreFilter("hasRole('ROLE_ADMIN') || "            + "filterObject.username == principal.username")    public void test2(List<User> users){        for(User u : users){            System.out.println(u.getUsername());        }    }}
  • 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

filterObject对象引用的是传入或传出的List中的某一个元素。

原创粉丝点击