Spring Security自定义身份验证过滤器和授权

时间:2018-07-02 22:47:33

标签: spring authentication spring-security filter authorization

我已经实现了自定义身份验证过滤器,并且效果很好。在设置会话并将身份验证对象添加到安全上下文后,我使用外部身份提供程序并重定向到我最初请求的URL。

安全配置

@EnableWebSecurity(debug = true)
@Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {

    // this is needed to pass the authentication manager into our custom security filter
    @Bean
    @Override
    AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean()
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                //.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
                .antMatchers("/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(new CustomSecurityFilter(authenticationManagerBean()), UsernamePasswordAuthenticationFilter.class)
    }
}

过滤逻辑

目前,我的自定义过滤器(一旦确认身份)只是对角色进行硬编码:

SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ")
                return new PreAuthenticatedAuthenticationToken(securityUser, null, [myrole])

然后将该身份验证对象(上面返回)添加到我的SecurityContext中,然后重定向到所需的端点:

SecurityContextHolder.getContext().setAuthentication(authentication)

控制器端点

  @RequestMapping(path = '/admin/test', method = GET, produces = 'text/plain')
  String test(HttpServletRequest request) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication()

    String roles = auth.getAuthorities()
    return "roles: ${roles}"
  }

然后,此端点在浏览器中产生以下响应:

  

“角色:[METADATA_CURATORZ]”

太好了。因此,我的身份验证和向用户应用角色非常有效。

现在,如果我从安全配置中取消注释此行:

//.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")

即使我们已经证明角色已设置,我也无法再访问该资源并获得403。

这对我来说似乎完全荒谬可笑,但我不是Spring Security专家。

我可能缺少一些非常简单的内容。有什么想法吗?

我有一些问题:

  • 是否需要将我的自定义过滤器放置在特定的内置过滤器之前,以确保执行该过滤器后进行授权步骤?
  • 在请求周期中何时进行antMatcher / hasRole检查?
  • 我是否需要更改安全配置链中正在调用的顺序,并且在当前编写该配置时应该如何理解?显然没有按照我的想法做。

1 个答案:

答案 0 :(得分:1)

  

是否需要将我的自定义过滤器放置在特定的内置过滤器之前,以确保在执行该过滤器之后进行授权步骤?

您的过滤器必须位于FilterSecurityInterceptor之前,因为这是进行授权和认证的地方。此过滤器是最后一个要调用的过滤器。

现在,确定过滤器的最佳位置在哪里,这确实取决于。例如,您确实希望过滤器位于AnonymousAuthenticationFilter之前,因为如果没有过滤器,那么在调用过滤器时,未经身份验证的用户将始终被AnonymousAuthenticationToken进行“身份验证”。

您可以在FilterComparator中查看过滤器的默认顺序。 AbstractPreAuthenticatedProcessingFilter几乎与您正在执行的操作相对应-其在过滤器中的位置使您可以将其放在哪里。无论如何,过滤器的顺序应该没有问题。

  

在请求周期中何时进行antMatcher / hasRole检查?

所有这些都发生在FilterSecurityInterceptor中,更确切地说,发生在其父AbstractSecurityInterceptor中:

protected InterceptorStatusToken beforeInvocation(Object object) {

    Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
            .getAttributes(object);

    if (attributes == null || attributes.isEmpty()) {
        ...
    }

    ...

    Authentication authenticated = authenticateIfRequired();

    // Attempt authorization
    try {
        this.accessDecisionManager.decide(authenticated, object, attributes);
    }
    catch (AccessDeniedException accessDeniedException) {

        ...

        throw accessDeniedException;
    }

其他信息: 本质上,FilterSecurityInterceptor具有包含ExpressionBasedFilterInvocationSecurityMetadataSource的{​​{1}}。在运行时,将根据Map<RequestMatcher, Collection<ConfigAttribute>>检查您的请求,以查看是否有任何Map键匹配。如果是,则将RequestMatcher传递到Collection<ConfigAttribute>,该访问最终会授予或拒绝访问。默认的AccessDecisionManagerAccessDecisionManager,其中包含处理AffirmativeBased集合的对象(通常是WebExpressionVoter),并通过反射调用与您的{对应的ConfigAttribute {1}}对SpelExpression初始化的"hasRole('METADATA_CURATORZ')"对象。

  

我是否需要更改安全配置链中正在调用的顺序,并且在我目前编写该配置时应该如何理解?显然没有按照我的想法做。

否,您的过滤器应该没有任何问题。顺便提一句,除了您的SecurityExpressionRoot方法中具有的功能之外,您扩展的Authentication还具有一些默认值:

configure(HttpSecurity http)

如果您想确切了解它们的作用以及添加的过滤器,可以查看WebSecurityConfigurerAdapter

问题

执行以下操作时:

http
    .csrf().and()
    .addFilter(new WebAsyncManagerIntegrationFilter())
    .exceptionHandling().and()
    .headers().and()
    .sessionManagement().and()
    .securityContext().and()
    .requestCache().and()
    .anonymous().and()
    .servletApi().and()
    .apply(new DefaultLoginPageConfigurer<>()).and()
    .logout();

...搜索的角色是HttpSecurity。为什么? .authorizeRequests() .antMatchers("/admin/test").hasRole("METADATA_CURATORZ") 的静态"ROLE_METADATA_CURATORZ"方法最终处理ExpressionUrlAuthorizationConfigurer

hasRole(String role)

因此您的授权表达式变为"METADATA_CURATORZ",最终在if (role.startsWith("ROLE_")) { throw new IllegalArgumentException( "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'"); } return "hasRole('ROLE_" + role + "')"; } 上调用方法"hasRole('ROLE_METADATA_CURATORZ'",该方法进而在{{1}中搜索角色hasRole('ROLE_METADATA_CURATORZ') }的权限。

解决方案

更改

SecurityExpressionRoot

收件人:

ROLE_METADATA_CURATORZ