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

时间:2013-07-17 12:20:51

标签: java spring web-services spring-mvc jax-ws

我有基于Spring MVC的Web应用程序,它调用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服务端口?

1 个答案:

答案 0 :(得分:0)

尝试将当前的http请求公开给spring。只需在RequestContextListener

中添加web.xml即可
<web-app>
...
<listener>
  <listener-class>
      org.springframework.web.context.request.RequestContextListener
  </listener-class>
</listener>
...
</web-app>

有关详情/选项,请参阅官方文档中的this entry

编辑。考虑控制器和paymentPort依赖关系之间生命周期的差异。控制器始终相同,但必须为每个新请求刷新paymentPort。所以你不能继续直接注射它。您需要为每个请求获取新的实例。您可以使用javax.inject.Provider界面执行此操作。