0

我有调用 Web 服务的基于 Spring MVC 的 Web 应用程序。Web 服务每次调用都需要基于 http 的身份验证。我在 applicationContext.xml 中保存 Web 服务代理配置:

<beans...

<bean id="paymentWebService"
      class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="customProperties">
        <ref local="jaxwsCustomProperties"/>
    </property>
    <property name="serviceInterface" value="com.azercell.paymentgateway.client.PaymentGatewayWebService"/>
    <property name="wsdlDocumentUrl"
              value="#{config.wsdlDocumentUrl}"/>
    <property name="namespaceUri" value="http://webservice.paymentgateway.azercell.com/"/>
    <property name="serviceName" value="PaymentGatewayWebServiceImplService"/>
    <property name="portName" value="PaymentGatewayWebServiceImplPort"/>
</bean>

我的控制器中有 Web 服务端口的实例字段:

@Controller
public class PaymentController {

@Resource(name = "paymentWebService")
private PaymentGatewayWebService paymentPort;

@ModelAttribute
public void ajaxAttribute(WebRequest request, Model model) {
    UtilMethods.authenticationWebServicePort(paymentPort);
    ...
}
...

@RequestMapping(value = "/massPayment", method = RequestMethod.POST)
public String massPayment(@RequestParam String amount, @RequestParam MultipartFile file, Model model, Locale locale) {
    ...

    WebServiceResponse response = paymentPort.massPayment(UtilMethods.getNewRequestId()
             , fileUploader, UtilMethods.getAmountInCoins(amountBigDecimal), null);

    ...      

    return SpringView.MASS_PAYMENT.toString(ajaxRequest);
   }
}

UtilMethods.authenticationWebServicePort 的代码:

public static void authenticationWebServicePort(PaymentGatewayWebService paymentPort) {
    String username = (String) RequestContextHolder.currentRequestAttributes().getAttribute(USERNAME_SESSION_VARIABLE_NAME, RequestAttributes.SCOPE_SESSION);
    String password = (String) RequestContextHolder.currentRequestAttributes().getAttribute(PASSWORD_SESSION_VARIABLE_NAME, RequestAttributes.SCOPE_SESSION);
    BindingProvider prov = (BindingProvider) paymentPort;
    prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
    prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
}

由于控制器是单例对象,因此每次请求重叠时都会出现问题,如果错误地恰好是一个用户,则可以使用属于另一个用户的用户名和密码的 Web 方法。

为了防止这种情况发生,我尝试在配置范围内设置 Web 服务端口请求,如下所示:

<bean id="paymentWebService"
  class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean" scope="request">

在这种情况下,我得到了错误:

    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot resolve reference to bean 'org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0' while setting constructor argument with key [2]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0': Cannot resolve reference to bean 'org.springframework.security.authentication.ProviderManager#0' while setting bean property 'authenticationManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.authentication.ProviderManager#0': Cannot resolve reference to bean 'org.springframework.security.config.authentication.AuthenticationManagerFactoryBean#0' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.authentication.AuthenticationManagerFactoryBean#0': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.authenticationManager': Cannot resolve reference to bean 'myAuthenticationProvider' while setting constructor argument with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myAuthenticationProvider': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'paymentWebService': Scope 'request' 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.

如何创建请求范围的 Web 服务端口?

4

1 回答 1

0

尝试将当前的 http 请求公开给 spring。只需添加RequestContextListener您的web.xml

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

有关更多详细信息/选项,请参阅官方文档中的此条目

编辑。想想控制器和paymentPort依赖在生命周期上的区别。控制器始终相同,但paymentPort必须为每个新请求刷新。所以你不能继续直接注入它。您需要为每个请求获取新实例。您可以使用javax.inject.Provider界面来完成。

于 2013-07-17T16:11:17.763 回答