代码之家  ›  专栏  ›  技术社区  ›  forgo

spring安全自定义身份验证筛选和授权

  •  2
  • forgo  · 技术社区  · 6 年前

    我已经实现了一个自定义的身份验证过滤器,它工作得很好。在设置会话并将身份验证对象添加到安全上下文后,我使用外部身份提供程序并重定向到最初请求的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}"
      }
    

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

    “角色:[元数据库]”

    伟大的。因此,我的身份验证和将角色应用于我的用户非常有效。

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

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

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

    我觉得这完全是胡说八道,而且很糟糕,但我不是弹簧安全专家。

    我可能遗漏了一些很简单的东西。有什么想法吗?

    我有几个问题:

    • 是否需要将自定义筛选器放在特定内置筛选器之前,以确保在执行该筛选器之后执行授权步骤?
    • 在请求周期中,何时进行antmatcher/hasrole检查?
    • 我是否需要更改我在安全配置链中调用的内容的顺序,以及如何理解当前编写的配置?它显然没有做我认为应该做的事。
    1 回复  |  直到 6 年前
        1
  •  1
  •   NatFar    5 年前

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

    你的过滤器 必须 来之前 FilterSecurityInterceptor ,因为这是进行授权和身份验证的地方。此筛选器是最后调用的筛选器之一。

    至于你的过滤器的最佳位置在哪里,那要看情况了。例如,你真的希望你的过滤器出现在 AnonymousAuthenticationFilter 因为如果没有,未经身份验证的用户将始终使用 AnonymousAuthenticationToken 在调用筛选器时。

    您可以签出过滤器的默认顺序 FilterComparator 是的。这个 AbstractPreAuthenticatedProcessingFilter 几乎与你正在做的事情相对应——它按照过滤器的顺序排列,让你知道你可以把你的东西放在哪里。无论如何,你的过滤器的顺序应该没有问题。

    在请求周期中,何时进行antmatcher/hasrole检查?

    所有这些都发生在 过滤器安全接收器 ,更确切地说,在它的父级 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;
        }
    

    额外信息: 实质上, 过滤器安全接收器 有一个 ExpressionBasedFilterInvocationSecurityMetadataSource 包含 Map<RequestMatcher, Collection<ConfigAttribute>> 是的。在运行时,将根据 Map 看看有没有 RequestMatcher 钥匙是匹配的。如果是,一个 Collection<ConfigAttribute> 传递给 AccessDecisionManager ,最终要么授予访问权,要么拒绝访问。违约 访问决策管理器 AffirmativeBased 包含对象(通常是 WebExpressionVoter )收集 ConfigAttribute 通过反射调用 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'" 最后调用方法 hasRole('ROLE_METADATA_CURATORZ') 安全表达式根 ,进而搜索角色 ROLE_METADATA_CURATORZ 身份验证 的权威。

    解决方案

    改变

    SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ");
    

    致:

    SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("ROLE_METADATA_CURATORZ");