Вызов защищенного метода из метода управляемого компонента JSF, аннотированного @PostConstruct

В управляемом компоненте JSF метод, аннотированный с помощью @PostConstruct - (javax.annotation.PostConstruct), вызывается, как только управляемый компонент JSF загружается, т. е. после того, как конструктор управляемого компонента завершает свое выполнение.

Когда я пытаюсь инициализировать java.util.List, вызывая защищенный метод в Spring DAO, что-то вроде следующего.

@Controller
@ManagedBean
@RequestScoped
public final class StateManagedBean extends LazyDataModel<StateTable>
{
    @Autowired
    private StateService stateService;

    @Autowired
    private SharableService sharableService;

    public StateManagedBean(){}

    @PostConstruct
    public void init()
    {
        countries=sharableService.getCountryList(); //Causes the exception.     
    }

    //The rest of the managed bean.
}

Я получаю следующее исключение.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'stateManagedBean': Invocation of init method failed; nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:399)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1481)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:626)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4797)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5291)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:657)
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:536)
    at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1462)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:791)
    at org.apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1445)
    at org.apache.catalina.manager.ManagerServlet.deploy(ManagerServlet.java:860)
    at org.apache.catalina.manager.ManagerServlet.doGet(ManagerServlet.java:357)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:581)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1822)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
Caused by: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:339)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:198)
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at $Proxy253.getCountryList(Unknown Source)
    at admin.mangedbean.StateManagedBean.init(StateManagedBean.java:55)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:344)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:295)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:130)
    ... 54 more

Простите меня. Это чем-то похоже на мой предыдущий вопрос. Причина, по которой я задаю этот вопрос, заключается в том, что я не понимаю, являются ли мои конфигурации безопасности Spring где-то шаткими или такие службы Spring не могут быть вызваны из методов управляемого компонента, аннотированных @PostConstruct. Это исключение действительно не вызывает в случае EJB, если я правильно помню.


Файл spring-security.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <http pattern="/utility/Login.jsf*" security="none"/>

    <http auto-config='true' use-expressions="true" disable-url-rewriting="true">
        <!--<remember-me key="myAppKey"/>-->
        <session-management session-fixation-protection="newSession">
            <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
        </session-management>

        <intercept-url pattern="/admin_side/**" access="hasRole('ROLE_ADMIN')" requires-channel="any"/>
        <http-basic />
        <anonymous />

        <form-login login-processing-url="/j_spring_security_check" login-page="/utility/Login.jsf" authentication-success-handler-ref="loginSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler"/> 
        <logout logout-success-url="/utility/Login.jsf" invalidate-session="true" delete-cookies="JSESSIONID"/>
    </http>

    <authentication-manager>
       <authentication-provider>
            <jdbc-user-service data-source-ref="dataSource"
               users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)"
               authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/>
       </authentication-provider>
    </authentication-manager>

    <beans:bean id="loginSuccessHandler" class="loginsuccesshandler.LoginSuccessHandler"/>
    <beans:bean id="authenticationFailureHandler" class="loginsuccesshandler.AuthenticationFailureHandler" />

    <global-method-security secured-annotations="enabled" proxy-target-class="false">
        <protect-pointcut expression="execution(* admin.dao.*.*(..))" access="ROLE_ADMIN"/>
    </global-method-security>
</beans:beans>

ИЗМЕНИТЬ:

Файл web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">        

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.xml
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <!--<param-value>Development</param-value>-->
        <param-value>Production</param-value>
    </context-param>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <listener>
        <description>
            This listener is here especially used to expose the request in org.springframework.security.web.authentication.AuthenticationSuccessHandler.
            By default, that state is exposed by DispatcherServlet.
            So, it is not available before the actual request enters DispatcherServlet (e.g. in Spring Security filters).
        </description>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>        

    <listener>
        <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>

    <security-constraint>
        <display-name>Restrict direct access to XHTML files</display-name>
        <web-resource-collection>
            <web-resource-name>XHTML files</web-resource-name>
            <url-pattern>*.xhtml</url-pattern>
        </web-resource-collection>
        <auth-constraint />
    </security-constraint> 

    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>/utility/Login.jsf</welcome-file>
    </welcome-file-list>

    <resource-ref>
        <res-ref-name>jdbc/social_networking</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
</web-app>

person Tiny    schedule 23.05.2013    source источник
comment
Можете ли вы показать нам свой файл web.xml? Одно из возможных объяснений вашей проблемы заключается в том, что Spring Security DelegatingFilterProxy не перехватывает запрос, который вызывает StateManagedBean. В качестве примечания: обычно безопасность метода применяется к уровню службы, а не к уровню DAO.   -  person Jawher    schedule 24.05.2013
comment
Отредактировал вопрос. Безопасность Spring работает хорошо, если удалить раздел <global-method-security>...</global-method-security> (в конце файла spring-security.xml). Это то, чего я долго не мог понять.   -  person Tiny    schedule 24.05.2013
comment
Я все еще не уверен, чего вы пытаетесь достичь, но: в веб-приложении большую часть времени вам не нужно использовать безопасность метода: URL-адресов перехвата будет достаточно. Кроме того, вы применили безопасность метода ко всем вашим методам dao. Ваш управляемый компонент вызывает метод службы. Эта служба зависит от DAO. Но поскольку последний защищен и поскольку пользователь еще не вошел в систему, Spring Security блокирует вызов, что вы и просили.   -  person Jawher    schedule 24.05.2013
comment
Люди используют ядро ​​​​Spring (обратите внимание, не Spring MVC) для среднего уровня вместо EJB: оно работает в контейнере сервлетов, таком как Tomcat, в отличие от EJB. В этом контексте Spring обычно управляет вашими бизнес-операциями и операциями доступа к базе данных. С другой стороны, JSF — это веб-инфраструктура на основе компонентов уровня представления, используемая для взаимодействия с пользователем. Это «примерно» аналогично Spring MVC, но последний представляет собой веб-фреймворк на основе запросов. Взаимодействие обычно достигается путем внедрения компонентов среднего уровня, управляемых Spring, в компоненты уровня представления, управляемые/используемые JSF.   -  person skuntsel    schedule 24.05.2013


Ответы (1)


Рассматриваемое исключение связано с сочетанием аннотаций перед компонентом. В моем случае этими bean-компонентами управляет Spring. Таким образом, за исключением аннотации @Controller, остальные две аннотации просто игнорируются, и в конечном итоге компонент имеет только одну аннотацию — @Controller.

Проблема в том, что аннотация регистрации bean-компонента Spring @Controller по умолчанию относится к области приложения (что касается Spring, на самом деле это singleton). Следовательно, рассматриваемый bean-компонент загружается при запуске приложения (когда не делается запрос и не создается сеанс), и метод защищенной службы sharableService.getCountryList(); вызывается, как только применяются инъекции зависимостей и вызывается конструктор bean-компонента, где SecurityContext не доступен как очевидное. Отсюда и исключение.

Теперь компонент должен быть изменен следующим образом.

import java.io.Serializable;
import admin.dao.service.StateService;
import admin.dao.service.SharableService;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;   

@Controller
@Scope("view")  //or other scope as needed.
public final class StateManagedBean extends LazyDataModel<StateTable> implements Serializable
{
    @Autowired
    private final transient StateService stateService=null;

    @Autowired
    private final transient SharableService sharableService=null;
    private List<Country>countries;

    public StateManagedBean(){}

    @PostConstruct
    public void init()
    {
        countries=sharableService.getCountryList(); //Works fine now.
    }

    //The rest of the managed bean.
}

Область просмотра была настроена конечно, см. также, так как Spring не имеет такой области видимости, как представление.

Я понял это спустя долгое время, когда BalusC прокомментировал многие из моих сообщений. (Злоупотреблять нечем, но некоторые статьи/блоги ввели меня в заблуждение :)).

person Tiny    schedule 19.07.2013
comment
Обратите внимание, что Spring злоупотребил термином singleton. Настоящий синглтон представляет собой нечто совершенно другое. - person BalusC; 23.07.2013