Spring在AuthenticationSuccessHandler中自动装配会话范围bean不起作用

Posted

技术标签:

【中文标题】Spring在AuthenticationSuccessHandler中自动装配会话范围bean不起作用【英文标题】:Spring autowireing a session scope bean in AuthenticationSuccessHandler is not working 【发布时间】:2017-03-15 18:59:42 【问题描述】:

我正在使用 Spring Security,我想在用户成功登录后在会话中初始化一个对象 User

安全配置如下:

@Configuration
@EnableWebSecurity
@PropertySource("classpath://configs.properties")
public class SecurityContextConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private Environment env;

    @Autowired
    SimpleUrlAuthenticationSuccessHandler simpleUrlAuthenticationSuccessHandler;


    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception 

        auth.inMemoryAuthentication()
            .withUser(env.getProperty("security.user1.userid"))
            .password(env.getProperty("security.user1.pass"))
            .roles(env.getProperty("security.user1.role"));
    


    @Override   
    protected void configure(HttpSecurity http) throws Exception 

        http.authorizeRequests().antMatchers("/*.cm")
                .access("hasRole('ADMIN')")
                .and()
                .formLogin()
                    .loginPage("/public-page.cm")
                    .loginProcessingUrl("/j_spring_security_check")
                    .usernameParameter("j_username")
                    .passwordParameter("j_password")
                    .successHandler(simpleUrlAuthenticationSuccessHandler)
                    .failureUrl("/public-page-authentication-failure.cm")
                .and()
                    .logout()
                    .logoutSuccessUrl("/public-page.cm")
                    .invalidateHttpSession(true)
                    .logoutUrl("/j_spring_security_logout")
                .and()
                    .csrf().disable();

    

    /**
     * configure which patterns the spring security should not be applied
     */
    @Override
    public void configure(WebSecurity web) throws Exception 
        web.ignoring().antMatchers("/index.jsp", "/public-page.jsp", "/public-page.cm",
                "/public-page-authentication-failure.cm", "/images/**", "/css/**", "/js/**");
    


User

@Component
@Scope("session")
public class User 

    private String selectedSystem;
    private String selectedBank;

SimpleUrlAuthenticationSuccessHandler 为:

@Component
public class SimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler 
    protected Log logger = LogFactory.getLog(this.getClass());

    @Autowired
    private User user;

错误是:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:355)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)

我已将 RequestContextListener 添加到 Web 应用程序中,如下所示:

public class WebAppInitializer implements WebApplicationInitializer 

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException 
        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
        appContext.register(DatabaseContextConfig.class);


        servletContext.addListener(new ContextLoaderListener(appContext));
        servletContext.addListener(new RequestContextListener());

        //Add Spring security filter
        FilterRegistration.Dynamic springSecurityFilterChain = servletContext.addFilter(
                AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,  DelegatingFilterProxy.class);
        springSecurityFilterChain.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");

    


我已阅读How to retrieve a session-scoped bean inside AuthenticationSuccessHandler?,但没有帮助

当我尝试Autowire none 会话 bean 时,它工作正常。

知道如何解决吗?!

【问题讨论】:

【参考方案1】:

你的例外是关于你的用户 bean,你给它一个会话范围。您似乎错过了会话​​范围的一些配置。

在 Spring MVC 中,我们有额外的范围,因为我们正在使用 Web 应用程序上下文,额外的范围是:会话范围、请求范围、应用程序范围。

我通常使用 XML 配置,所以我的答案将是那种格式,你可以在之后翻译成 java 配置。

您需要在 web.xml 中添加一个监听器,如下所示:

<listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

这个监听器会与每个进来的请求相关联。

现在,对于您想要拥有会话作用域的 bean,您需要向其添加一个作用域代理,为此您需要将 aop 命名空间添加到您的配置文件中,并且:

<bean class="user.package.User" scope="session">
    <aop:scoped-proxy/>
</bean>

这个 bean 应该在 dispatcher-servlet.xml 文件中

到此为止,一切准备就绪。

在这里查看如何在 java 配置中使用 scoped-proxy:

Spring JavaConfig of aop:scoped proxy

【讨论】:

在服务器启动期间我遇到这个错误 名为“用户”的 Bean 应该是 [user.package.User] 类型,但实际上是 [com.sun.proxy.$Proxy36 ] 我不是说你真的使用了user.package.User,你需要使用你的包来代替。如果您的 User 类在 bla.bla.foo 的包中,则使用 bla.bla.foo.User 我当然用过我的包。看来错误是因为为User bean设置了代理模式 那么,有什么问题? 亲爱的@Moshe 我已经在上面的答案中发送了错误。 .. 弹簧无法初始化。让我发送完整的错误行... 创建名称为“simpleUrlAuthenticationSuccessHandler”的 bean 时出错:通过字段“用户”表达的不满足依赖关系:名为“用户”的 Bean 应为 [foo.bar.User] 类型,但实际上是 [com.sun.proxy.$Proxy36] 类型;嵌套异常是 org.springframework.beans.factory.BeanNotOfRequiredTypeException:名为 'user' 的 Bean 应该是 [foo.bar.User] 类型,但实际上是 [com.sun.proxy.$Proxy36] 类型

以上是关于Spring在AuthenticationSuccessHandler中自动装配会话范围bean不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Spring 如何在 Spring Boot 中设置数据源

如何在spring中配置定时任务

Spring 4 安全、MySQL、c3p0 连接。登录在 Spring 5 中有效,但在 Spring 4 中无效

Spring源码学习:第1步--在Spring源码中添加最简单的Demo代码

spring框架有啥用?

避免在单个 jar 中合并多个 spring 依赖项时覆盖 spring.handlers/spring.schemas 的想法