如何在自定义过滤器中使用Java配置注入AuthenticationManager

93

我正在使用Spring Security 3.2和Spring 4.0.1

我正在将一个xml配置转换为Java配置。当我在我的过滤器中用@Autowired注释AuthenticationManager时,我会得到一个异常。

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

我尝试过注入AuthenticationManagerFactoryBean,但也出现了类似的异常。

这是我正在使用的XML配置。

<?xml version="1.0" encoding="UTF-8"?> <beans ...>
    <security:authentication-manager id="authenticationManager">
        <security:authentication-provider user-service-ref="userDao">
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <security:http
            realm="Protected API"
            use-expressions="true"
            auto-config="false"
            create-session="stateless"
            entry-point-ref="unauthorizedEntryPoint"
            authentication-manager-ref="authenticationManager">
        <security:access-denied-handler ref="accessDeniedHandler"/>
        <security:custom-filter ref="tokenAuthenticationProcessingFilter" position="FORM_LOGIN_FILTER"/>
        <security:custom-filter ref="tokenFilter" position="REMEMBER_ME_FILTER"/>
        <security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')"/>
        <security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')"/>
        <security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')"/>
        <security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')"/>
    </security:http>

    <bean class="com.unsubcentral.security.TokenAuthenticationProcessingFilter"
          id="tokenAuthenticationProcessingFilter">
        <constructor-arg value="/rest/user/authenticate"/>
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
        <property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
    </bean>

</beans>

这是我正在尝试的Java配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .exceptionHandling()
                    .authenticationEntryPoint(authenticationEntryPoint)
                    .accessDeniedHandler(accessDeniedHandler)
                    .and();
        //TODO: Custom Filters
    }
}

这是自定义过滤器类。令我困扰的是设置AuthenticationManager的setter行。

@Component
public class TokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {


    @Autowired
    public TokenAuthenticationProcessingFilter(@Value("/rest/useAuthenticationManagerr/authenticate") String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
      ...
    }

    private String obtainPassword(HttpServletRequest request) {
        return request.getParameter("password");
    }

    private String obtainUsername(HttpServletRequest request) {
        return request.getParameter("username");
    }

    @Autowired
    @Override
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }

    @Autowired
    @Override
    public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
        super.setAuthenticationSuccessHandler(successHandler);
    }

    @Autowired
    @Override
    public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
        super.setAuthenticationFailureHandler(failureHandler);
    }
}

我可以问一下在 Override 上面的 Autowired 是做什么用的吗?我以前从未见过这种用法。这个是和什么连通的呢? - Stephane
你是如何添加自定义过滤器的?我创建了我的自定义过滤器和认证提供者,但不知道如何配置它们一起工作。这是我的问题链接 https://dev59.com/iYvda4cB1Zd3GeqPYFr2 - PaintedRed
3个回答

215

重写 WebSecurityConfigurerAdapter 中的方法 authenticationManagerBean ,将使用 configure(AuthenticationManagerBuilder) 构建的 AuthenticationManager 作为 Spring bean 暴露出来:

例如:

   @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
   @Override
   public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
   }

2
将使用configure(AuthenticationManagerBuilder)构建的AuthenticationManager公开为Spring bean。 - Roger
2
@Roger,为什么我们需要手动公开 AuthenticationManager? - qxixp
13
您只能自动装配一个由Spring管理的Bean。如果它没有作为Bean暴露出来,您就无法进行自动装配。 - Roger
2
这个答案真正帮助我的是 "name = BeanIds.AUTHENTICATION_MANAGER"。没有它,在我的环境中至少无法工作。 - Isthar
3
在Spring Security OAuth2中,这种方式不适用于定制过的过滤器。类似@Bean super.authenticationManagerBean()这样的方式无法构建ClientDetailsUserDetailsServiceDaoAuthenticationProvider。这与http.getSharedObject(AuthenticationManager.class)的方式有所不同。 - phxism
显示剩余4条评论

2

除了Angular University上面提到的内容,您可能还想使用@Import将@Configuration类聚合到其他类中(在我的情况下是AuthenticationController):

@Import(SecurityConfig.class)
@RestController
public class AuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
//some logic
}

Spring文档关于使用@Import聚合@Configuration类的内容:链接


0
当我在同一个类中@Bean了AuthenticationManager并@Autowired它时,需要激活循环引用,但这对于CDI来说是相当的。

1
我也一样,但有一个解决方法:如果您使用 @Autowiring 自动装配了 BeanFactory,则可以动态解析 Bean。示例代码如下:private AuthenticationManager getAuthenticationManager() { if (this.authenticationManager == null) { this.authenticationManager = beanFactory.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class); } return this.authenticationManager; } - Alexander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接