Spring-Security-文档笔记之授权

1. 授权框架

1.1 GrantedAuthority
String getAuthority();

AuthenticationManagerGrantedAuthority存入到Authentication中, 然后被AccessDecisionManager读取以决定是否授权.
SimpleGrantedAuthorityGrantedAuthority的一个实现, 它将String转换为权限.

1.2 Pre-Invocation Handling

Spring Security提供了拦截器来控制对安全对象(如方法调用或web请求)的访问。AccessDecisionManager进行调用前处理.
AccessDecisionManager由AbstractSecurityInterceptor调用,负责做出最终的访问控制决策。AccessDecisionManager接口包含三个方法:

void decide(Authentication authentication, Object secureObject,
    Collection attrs) throws AccessDeniedException;

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);
  • 基于投票的AccessDecisionManager实现
    image.png

    使用这种方式, 通过轮询一系列AccessDecisionVoter. 然后AccessDecisionManager根据结果来决定是否抛出AccessDeniedException.
// AccessDecisionVoter 
int vote(Authentication authentication, Object object, Collection attrs);

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

vote方法返回int, 可能的值有: ACCESS_ABSTAIN, ACCESS_DENIED, ACCESS_GRANTED. ACCESS_ABSTAIN表示弃权, ACCESS_DENIED表示拒绝, ACCESS_GRANTED表示授权.

Spring Security提供了三个AccessDecisionManager实现来统计投票。allowIfEqualGrantedDeniedDecisions则表示当投票相等或全部弃权时的控制行为.

  • ConsensusBased:
    根据ACCESS_GRANTED和ACCESS_DENIED各自的票数多少来判断. 哪种多就返回哪种结果.
  • AffirmativeBased:
    只要有一个ACCESS_GRANTED, 则授权.
  • UnanimousBased:
    所有的都投ACCESS_GRANTED则授权. 忽略弃权投票.

Spring Security常用投票器:

  • AuthenticatedVoter
    它可区别匿名用户, 完全授权用户和rememberme用户. 有的网站会限制rememberme用户的操作. 根据IS_AUTHENTICATED_FULLY , IS_AUTHENTICATED_REMEMBERED, IS_AUTHENTICATED_ANONYMOUSLY来判断.
  • RoleVoter
    判断用户是否具有相应的角色. 如果存在以ROLE_开头的属性, 则进行投票, 如果没有, 则投弃权票.
  • RoleHierarchyVoter
    角色继承
1.3 After Invocation Handling

虽然在继续进行安全调用之前,AbstractSecurityInterceptor会调用AccessDecisionManager,但有些应用程序需要一种方法来修改安全调用返回的对象。虽然也可使用AOP来实现, 但Spring提供了一些钩子. 这个功能是由AfterInvocationManager来实现的.

image.png

AfterInvocationManager有一个实现AfterInvocationProviderManager, 它会轮询AfterInvocationProvider, 每个provider都可以修改返回对象或抛出一个AccessDeniedException.

1.4 角色继承

    


    
        
            ROLE_ADMIN > ROLE_STAFF
            ROLE_STAFF > ROLE_USER
            ROLE_USER > ROLE_GUEST
        
    

2. 使用FilterSecurityInterceptor授权HttpServletRequest

FilterSecurityInterceptor作为一个Security Filter, 用于向所有请求提供授权.

image.png

  1. FilterSecurityInterceptor从SecurityContextHolder中获取Authentication对象.
  2. FilterSecurityInterceptor 创建一个FilterInvocation对象.
  3. 将FilterInvocation对象传递给SecurityMetadataSource以获取ConfigAttribute.
  4. 将Authentication, FilterInvocation, ConfigAttribute传递给AccessDecisionManager.

默认要求所有的请求都需要认证.

// java
protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .authorizeRequests(authorize -> authorize
            .anyRequest().authenticated()
        );
}

//xml

    

自定义配置:

// java
protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .authorizeRequests(authorize -> authorize                                  
            .mvcMatchers("/resources/**", "/signup", "/about").permitAll()         
            .mvcMatchers("/admin/**").hasRole("ADMIN")                             
            .mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")   
            .anyRequest().denyAll()                                                
        );
}

// xml
 
    
    
    

     
     
     

当使用hasRole方法时,可省略前缀.

3. 基于表达式的访问控制

表达式的计算是通过一系列root对象实现的, 其中SecurityExpressionRoot是root对象的基类.

3.1 常见表达式
表达式 描述
hasRole("") 有指定角色返回true
hasAnyRole(String… roles) 有指定的任意一个角色返回true. hasAnyRole('admin', 'user')
hasAuthority(String authority) 有指定权限则返回true. hasAuthority('read')
hasAnyAuthority(String… authorities) 有指定权限中的一个就返回true.
principal 当前用户的主体对象,可在表达式中直接使用
authentication authentication对象, 可直接使用
permitAll 允许所有请求
denyAll 拒绝所有
isAnonymous() 是否为匿名用户
isRememberMe() 是否为rememberme用户
isAuthenticated() 如果不是匿名用户则返回true
isFullyAuthenticated() 是否为用户名密码登录的用户.
hasPermission(Object target, Object permission) hasPermission(domainObject, 'read')
hasPermission(Object targetId, String targetType, Object permission) hasPermission(1, 'com.example.domain.Message', 'read')
3.2 web安全表达式

    
    ...

表达式hasIpAddress是一个额外的内置表达式,它是特定于web安全的。它由WebSecurityExpressionRoot类定义

3.3 方法安全表达式

有四种注解支持表达式属性,以允许调用前和调用后的授权检查,并支持对提交的集合参数或返回值进行过滤。它们的使用是通过global-method-security命名空间元素启用的:


  1. @PreAuthorize
    决定方法是否被调用.
@PreAuthorize("hasRole('USER')")
public void create(Contact contact);

@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);

可以引用方法参数. 有多种方式发现方法参数, spring security默认使用DefaultSecurityParameterNameDiscoverer发现方法参数名.
当方法只有一个参数时, 可通过@P注解指定参数名.

@PreAuthorize("#c.name == authentication.name")
public void doSomething(@P("c") Contact contact);

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

当方法有多个参数时, 可用Spring Data的@Param指定参数名

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

希望在调用方法之后执行访问控制检查。这可以使用@PostAuthorize注释实现。要访问方法的返回值,请在表达式中使用内置名称returnbject。

  1. @PreFilter and @PostFilter
    Spring Security支持使用表达式过滤集合、数组、映射和流。这最好是通过优化数据检索来实现过滤.

方法安全元注解:

@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("#contact.name == authentication.name")
public @interface ContactPermission {}

你可能感兴趣的:(Spring-Security-文档笔记之授权)