Spring Security:为什么我的自定义AccessDecisionVoter没有被调用

时间:2016-01-01 01:30:56

标签: spring-mvc spring-security spring-java-config

我正在尝试使用自定义AccessDecisionVoter进行网址授权。我没有收到任何错误,调试显示我的选民在启动时被选中。但是,在运行时,不会调用vote方法,从而允许每个经过身份验证的用户完全访问。

请注意,我不需要方法安全性。我也没有使用XML配置。这排除了互联网上有关此主题的每个例子。

@Configuration
@EnableWebSecurity
@EnableWebMvc
@ComponentScan
@Order(-10)
public class HttpSecurityConfig extends WebSecurityConfigurerAdapter {
    @Value("${trusted_ports}")
    private List<Integer> trustedPorts;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ServiceIdAwareVoter serviceIdAwareVoter;

    RequestMatcher requestMatcher = new OrRequestMatcher(
        // @formatter:off
        new AntPathRequestMatcher("/**", GET.name()),
        new AntPathRequestMatcher("/**", POST.name()), 
        new AntPathRequestMatcher("/**", DELETE.name()),
        new AntPathRequestMatcher("/**", PATCH.name()), 
        new AntPathRequestMatcher("/**", PUT.name())
        // @formatter:on
    );

    @Override
    protected UserDetailsService userDetailsService() {
        return userDetailsService;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(preAuthProvider());
        auth.authenticationProvider(authProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.
            httpBasic().and().
            authorizeRequests().anyRequest().fullyAuthenticated().
            accessDecisionManager(accessDecisionManager()).and().
            csrf().disable().
            logout().disable().
            exceptionHandling().and().
            sessionManagement().sessionCreationPolicy(STATELESS).and().
            anonymous().disable().
            addFilterAfter(preAuthFilter(), X509AuthenticationFilter.class).
            addFilter(authFilter());
        // @formatter:on
    }

    AccessDecisionManager accessDecisionManager() {
        return new UnanimousBased(ImmutableList.of(serviceIdAwareVoter));
    }

    Filter preAuthFilter() throws Exception {
        PreAuthenticationFilter preAuthFilter = new PreAuthenticationFilter(trustedPorts);

        preAuthFilter.setAuthenticationManager(super.authenticationManager());

        return preAuthFilter;
    }

    PreAuthenticatedAuthenticationProvider preAuthProvider() {
        PreAuthenticatedAuthenticationProvider preAuthProvider = new PreAuthenticatedAuthenticationProvider();
        UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> userDetailsServiceWrapper = new UserDetailsByNameServiceWrapper<>();

        userDetailsServiceWrapper.setUserDetailsService(userDetailsService());

        preAuthProvider.setPreAuthenticatedUserDetailsService(userDetailsServiceWrapper);

        return preAuthProvider;
    }

    Filter authFilter() throws Exception {
        AppIdAppKeyAuthenticationFilter authFilter = new AppIdAppKeyAuthenticationFilter(requestMatcher);
        authFilter.setAuthenticationFailureHandler(new ExceptionStoringAuthenticationFailureHandler());
        authFilter.setAuthenticationSuccessHandler(new UrlForwardingAuthenticationSuccessHandler());

        authFilter.setAuthenticationManager(authenticationManagerBean());

        return authFilter;
    }

    AuthenticationProvider authProvider() {
        AppIdAppKeyAuthenticationProvider authProvider = new AppIdAppKeyAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());

        return authProvider;
    }

2 个答案:

答案 0 :(得分:5)

<强>背景

经过几个小时的调试,我发现了问题的根本原因,这个问题非常深刻。其中一部分原因是Spring Security Java配置文档记录很差(为此我打开了JIRA ticket)。他们以及大多数在线示例都是从XML配置中复制粘贴的,而自2010年以来世界已经停止使用Spring XML配置。另一部分原因是REST服务安全性是Spring Security设计中的事后想法,没有一流的支持来保护没有登录页面,错误页面和通常的视图层的应用程序。最后但并非最不重要的是,我的应用程序中有几个(错误的)配置,它们汇集在一起​​,创造了令人难以置信的复杂风暴。

技术背景:

使用authorizeRequests()配置ExpressionUrlAuthorizationConfigurer,最终设置UnanimousBased AccessDecisionManager WebExpressionVoter。如果身份验证成功,则从AccessDecisionManager调用此FilterSecurityInterceptor(如果用户首先未通过身份验证,则显然没有授权点)。

<强>的问题:

  • 在我的AbstractAnnotationConfigDispatcherServletInitializer子类中,基本上是web.xml的Java版本,我配置过滤器不拦截转发请求。我不打算在这里进入 why 。对于感兴趣的人来说,这是一个如何完成的例子:

    private Dynamic registerCorsFilter(ServletContext ctx) {
        Dynamic registration = ctx.addFilter("CorsFilter", CorsFilter.class);
    
        registration.addMappingForUrlPatterns(getDispatcherTypes(), false, "/*");    
        return registration;
    }
    
    private EnumSet<DispatcherType> getDispatcherTypes() {
        return (isAsyncSupported() ? EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC)
      : EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
    }
    

如果您从调度员类型集中取出DispatcherType.FORWARD,则注册的过滤器不会为此类请求启动。

  • 我的问题中显示的authFilterUsernamePasswordAuthenticationFilter扩展而来,并且AuthenticationSuccessHandler在成功验证后将请求转发到目标网址。默认的Spring实现使用SavedRequestAwareAuthenticationSuccessHandler来重定向到网页,这在REST应用程序的上下文中是不需要的。
  • 由于上述两个原因,在成功进行身份验证后未调用FilterSecurityInterceptor,而后者又跳过授权链,导致原始帖子出现问题。

<强>修正:

  • 从Web应用初始化程序中删除自定义调度程序配置。
  • 不要从AuthenticationSuccessHandler转发或重定向。请让请求采用它的自然过程。
  • 自定义投票人的vote方法如下所示:

    public int vote(Authentication authentication, FilterInvocation fi,
            Collection<ConfigAttribute> attributes) {
    }
    

我的案例中的attributes,如我的原始帖子所示,是字符串表达式fullyAuthenticated。我没有使用它进行授权,因为我已经知道用户已通过身份验证流程中的各种过滤器进行了身份验证。

我希望这可以作为所有那些因Spring Security Java配置缺乏文档而遭受痛苦的人的文档。

答案 1 :(得分:0)

您的配置说您允许访问完全通过身份验证的用户:

authorizeRequests().anyRequest().fullyAuthenticated().

您告诉Spring Security只要完全通过身份验证就可以授予对任何请求的访问权限。你的目标是什么?你是如何通过角色/权限来限制访问的?我猜这是你在自定义选民豆内的命令吗?

通常,当你的安全级别存在冲突时,选民bean会发挥作用,例如,在这里你说所有请求都具有完全访问权限,但是如果你的代码遇到方法级安全性的方法(不是一个非常真实的世界)示例):

@PreAuthrorize("permitNone")
public void someMethod{
   ...
}

你将有选民进场,因为你的java安全配置说“授予所有人访问权限”(投票赞成访问)但这种方法注释是“授予访问权限”(投票否访问) 。

在您的情况下,没有任何可投票权,您授予所有人访问权。