Spring Security教程 Vol 7. 访问规则ConfigAttribute

第七期 访问规则ConfigAttribute

从这一期开始,咱们将分别对访问控制模块主要的三个组件进行介绍。首先,咱们将对访问控制的配置部分展开说明,在这一期咱们将对如下内容进行讲解:java

  1. ConfigAttribute的经常使用组件
  2. WebExpressionConfigAttribute
  3. SecurityConfig
  4. PostInvocationExpressionAttribute

1、ConfigAttribute的经常使用组件

ConfigAttribute做为访问控制模板用于“描述”规则的组件,最多见的方式主要是两种一种是基于注解的一种是基于JavaConfig在Web配置中进行配置的。 其中Spring Security对应方法级的注解主要又能够分为两类: 第一类 @Secured 注解 - secured-annotations 第二类 @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter - pre-post-annotationsbash

ConfigAttribute的经常使用组件

1.1 WebExpressionConfigAttribute

WebExpressionConfigAttribute是咱们最先也是最多见的访问控制方式。好比下面的代码形式ide

http.authorizeRequests()
        .antMatchers("/").access("hasAnyRole('ANONYMOUS', 'USER')")
        .antMatchers("/login/*").access("hasAnyRole('ANONYMOUS', 'USER')")
        .antMatchers("/logout/*").access("hasAnyRole('ANONYMOUS', 'USER')")
        .antMatchers("/admin/*").access("hasRole('ADMIN')")
        .antMatchers("/events/").access("hasRole('ADMIN')")
        .antMatchers("/**").access("hasRole('USER')")
复制代码

基于Web表达是能够对目标URL的模式进行访问控制,而控制检查的规则最多见两种方式一种是基于角色(Role-Based),另外一种是基于表达式(Expressions-Based)。 演示代码中使用的是access的表达式进行控制,一样的也能够直接使用下面的形式经过角色来达到一样的效果。工具

.antMatchers("/logout/*").hasAnyRole("USER","ANONYMOUS")
复制代码

能够说基于Web表达式对于Web资源的控制是咱们最长见的方式。 除此之外,access还有另一个很酷炫的功能就是经过表达式将判断的方法委托表达式内的方法进行判断。若是最终表达式返回false则会返回403禁止访问,true则表示受权访问。 举个例子,假设咱们系统内针对/user路径须要对用户名进行判断,若是用户名不是‘user’则不能够访问。咱们把对应判断的代码写在一个CustomWebSecurity的Bean中。oop

@Component()
public class CustomWebSecurity {
        public boolean check(Authentication authentication) {
            return  (authentication.getName().equals("user"));
        }
}
复制代码

而后咱们经过修改Web表达式我访问控制向/user的路径增长这样一个访问控制的表达式。post

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/user").access("@customWebSecurity.check(authentication)")
                .and()
                .formLogin();
    }
复制代码

最后,咱们为了达到测试的目的对应的添加两个用户测试

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser(User.withUsername("user").password("{noop}password").roles("USER"));
        auth.inMemoryAuthentication().withUser(User.withUsername("test").password("{noop}password").roles("USER"));
    }
复制代码

整个测试的Security配置类以下:ui

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/user").access("@customWebSecurity.check(authentication)")
                .and()
                .formLogin();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser(User.withUsername("user").password("{noop}password").roles("USER"));
        auth.inMemoryAuthentication().withUser(User.withUsername("test").password("{noop}password").roles("USER"));
    }
}
复制代码

而后,咱们分别登陆用户进行测试,首先咱们登陆user的帐户。 lua

登陆界面
输入user和password以后,咱们能够正确的访问/user的页面。
正确的/user页面
但当咱们使用test和password以test用户身份登陆时,由于没法经过access中的check方法对用户名的检查,因此咱们便会获得一个403禁止访问的错误。

image.png

1.2 SecurityConfig

说完了,最经常使用的WebExpressionConfigAttribute了解了基于角色与表达式对目标地址进行访问控制的配置以后,接下来咱们分别对两个基于方法级进行控制的配置展开介绍。 首先咱们须要在相关的JavaConfig中激活相关的方法级的验证。spa

@EnableGlobalMethodSecurity(securedEnabled = true) // <--打开Secured注解配置
public class MethodSecurityConfig {
// ...
}
复制代码

以后咱们即可以在咱们须要进行权限验证的地方增长相关的注解和规则:

public interface BankService {

@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);

@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();

@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}
复制代码

@Secured("ROLE_TELLER")中的表达式即是验证规则,全部的AccessDecisionVoter都会一次检查是否对该表达式支持,好比这个例子里,RoleVoter便会对该规则进行表决:若是当前访问的用户拥有TELLER的角色,那么就能够继续执行该方法;若是不没有对应的角色,那么就会返回一个403的错误。 同理@Secured("IS_AUTHENTICATED_ANONYMOUSLY")对应的即是AuthenticatedVoter。 利用这种特性,咱们也能够经过自定义相关表达式与客制化对应的AccessDecisionVoter来完成特有的访问控制逻辑。

1.3 PostInvocationExpressionAttribute

最后则是利用切面进行访问控制逻辑的@Pre与@Post注解。一样的若是须要使用这种方法级的配置,也须要激活对应的配置。

@EnableGlobalMethodSecurity(prePostEnabled = true) // <--打开Pre与Post注解配置
public class MethodSecurityConfig {
// ...
}
复制代码

与@Secured注解不一样,若是激活了改配置,则可使用如下四个注解对Java方法级进行访问控制和处理:

  • @PreAuthorize
  • @PreFilter
  • @PostAuthorize
  • @PostFilter

@PreAuthorize

其中比较经常使用的多是@PreAuthorize注解,@PreAuthrize在功能上与@Secured基本是一致的,即是在运行被注解的方法事前先对规则进行投票,若是经过以后则受权进行访问。相比@Secured而言@PreAuthrize并非依靠不一样的AccessDecisionVoter来完成,而是依靠其编写的各类表达式的值进行判断。 好比咱们对方法入参的用户名进行判断,则可以使用

@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);
复制代码

或者

@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);
复制代码

@PreAuthorize的表达式是很是强大工具,毕竟注入Authentication对象的方法在写测试用例的时候就很是的痛苦了……

public void doSomething(Contact contact,@Autowired Authentication authentication){
    if (contact.name == authentication.name){   
    ///
   }
};
复制代码

相对先验的@PreAuthorize来讲后验的@PostAuthorize注解的使用场景就基本很罕见了。是一个几乎能够忽略的注解。

@PreFilter & @PostFilter

接下来讲两个其余教程中都不太提到的Spring Security中的两个Filter注解。这两个注解的本质是经过在方法级编写了一个Spring-EL表达式对方法使用和返回的集合进行过滤。 好比咱们最多见的场景即是不一样用户可能生成的菜单可能不一样,咱们可能会给每个菜单都赋予一个Permission权限。可是若是在这里展开怕是3000个字也说不清楚,有机会单独在ACL部分作一个实战型的说明。 这边举个简单的例子,假设咱们如今对用户使用的一个集合进行过滤,若是规则是只可访问奇数ID的对象。换言之,过滤的规则则是“当ID为偶数”。

@PreFilter(filterTarget="ids", value="filterObject%2==0")
 
   public void doSomething(List<Integer> ids) {
 
   }
复制代码

若是是对方法返回值的集合进行过滤,则须要使用@PostFilter

   @PostFilter("filterObject.id%2==0")
  public List<User> findAll() {

      List<User> userList = new ArrayList<User>();

      User user;

      for (int i=0; i<10; i++) {

         user = new User();

         user.setId(i);

         userList.add(user);

      }

      return userList;
   }
复制代码

在SpringSecurity中也内建了一部分表达式规则,如基于Permission对相应对象作权限验证过滤:

@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();
复制代码

而这一部分便设计了PermissionEvaluator权限评估器还完成相应进行目标领域对象操做所须要的权限逻辑。而这一部分则是在ACL客制化的重点。

PermissionEvaluator接口

结尾

这一期稍微详细的带你们蜻蜓点水般的了解了下Spring Security提供访问控制各类配置方法和其使用场景。 由于针对这一部分若是过分展开脱离实战场景也很是难掌握,因此这一期的真的就只是让你们了解Spring Security针对不一样的访问控制颗粒细度应该怎么配置,好比URL级、代码方法级、领域集合级别的权限过滤或者是客制化对应的控制逻辑。 下一期咱们将先对访问控制另一个核心,即如何针对配置进行处理的AccessDecisionVoter接口组件进行说明。 咱们下期再见。

相关文章
相关标签/搜索