使用 @Service 构造型创建 XsrfProtectedServiceServlet 会抛出 NullPointerException

Creating an XsrfProtectedServiceServlet with a @Service stereotype throws a NullPointerException

我正在尝试按照指南中的指南将 CSRF 保护添加到我们的 GWT RPC 层 GWT 文档。

我在创建扩展 XsrfProtectedServiceServlet 的 RPC 服务实现 bean 时遇到问题,因为它似乎是上下文 配置仅在 bean 创建后创建。尝试 运行 时出现以下 NullPointerException 应用程序:

ERROR [ContextLoader] : Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myServiceImpl' defined in file
[/path/to/project/exploded/WEB-INF/classes/za/co/example/server/MyServiceImpl.class]:
Invocation of init method failed; nested exception is java.lang.NullPointerException
.
.
.
Caused by: java.lang.NullPointerException
  at com.google.gwt.user.server.rpc.XsrfProtectedServiceServlet.init(XsrfProtectedServiceServlet.java:84)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1713)
  at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1650)
  at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1579)

RPC 服务的服务器端实现如下所示:

import com.google.gwt.user.server.rpc.XsrfProtectedServiceServlet;

@Service
public class MyServiceImpl extends XsrfProtectedServiceServlet implements MyService {
    // interface overriding methods
}

这是客户端界面:

import com.google.gwt.user.client.rpc.XsrfProtectedService;

public interface MyService extends XsrfProtectedService {
    // interface methods
}

注意:我省略了 MyServiceAsync,因为无需更改代码即可在我们的 RPC 调用中实施反 CSRF 令牌。

XsrfProtectedServiceServlet 尝试通过 getServletConfig() 访问 servlet 配置时抛出空指针,如下所示,即 getServletConfig() 的 return 值为空:

@Override
public void init() throws ServletException {
    super.init();
    // do not overwrite if value is supplied in constructor
    if (sessionCookieName == null) {
        // servlet configuration precedes context configuration
        sessionCookieName = getServletConfig().getInitParameter(
                XsrfTokenServiceServlet.COOKIE_NAME_PARAM);
        if (sessionCookieName == null) {
            sessionCookieName = getServletContext().getInitParameter(
                    XsrfTokenServiceServlet.COOKIE_NAME_PARAM);
        }
        if (sessionCookieName == null) {
            throw new IllegalStateException(
                    XsrfTokenServiceServlet.COOKIE_NAME_NOT_SET_ERROR_MSG);
        }
    }
}

为了让 Spring 使用 @Service 构造型正确注入我的 RPC 服务实现,我是否需要做额外的配置?

查看 class 抛出空指针的代码,这似乎是有问题的方法:

@Override
  public void init() throws ServletException {
    super.init();
    // do not overwrite if value is supplied in constructor
    if (sessionCookieName == null) {
      // servlet configuration precedes context configuration
      sessionCookieName = getServletConfig().getInitParameter(
          XsrfTokenServiceServlet.COOKIE_NAME_PARAM);
      if (sessionCookieName == null) {
        sessionCookieName = getServletContext().getInitParameter(
            XsrfTokenServiceServlet.COOKIE_NAME_PARAM);
      }
      if (sessionCookieName == null) {
        throw new IllegalStateException(
            XsrfTokenServiceServlet.COOKIE_NAME_NOT_SET_ERROR_MSG);
      }
    }
  }

抛出异常的行是:

sessionCookieName = getServletConfig().getInitParameter(
          XsrfTokenServiceServlet.COOKIE_NAME_PARAM);

因此 getServletConfig() 方法必须在加载应用程序上下文时返回 null。可能有一些方法可以确保在 servlet 配置存在之前 bean 不是 "wired in",但是,在您的情况下有一个简单的解决方案。由于 servlet 配置和上下文仅在 cookie 名称为 null 时才需要访问它,因此您可以通过在构造函数中指定 cookie 名称来绕过它,即:

public XsrfProtectedServiceServlet(String sessionCookieName) {
    this.sessionCookieName = sessionCookieName;
}

因此在您的情况下,在 MyServiceImpl 中您将添加:

public MyServiceImpl() {
   super("NAME_OF_THE_COOKIE_YOU_ARE_SPECIFYING_IN_WEB_XML_PROBABLY_JSESSIONID");
}