如何将类型添加到GWT的序列化策略白名单中?

35

GWT的序列化器对于java.io.Serializable有限支持,由于安全原因只支持白名单中的类型。例如此FAQ条目所述,您想要序列化的任何类型“必须包含在序列化策略白名单中”,该列表在编译时生成,但并未解释编译器如何决定将哪些内容放入白名单。

生成的列表包含许多标准库的类型,例如java.lang.Stringjava.util.HashMap。当尝试序列化实现了Serializable接口却不在白名单中的java.sql.Date时,会出现错误。如何将这种类型添加到列表中?

9个回答

30
有一个解决方法:定义一个新的Dummy类,其中包含您想要包含在序列化中的所有类型的成员字段。然后向您的RPC接口添加一个方法:
Dummy dummy(Dummy d);

实现就是这样:

Dummy dummy(Dummy d) { return d; }

而异步接口将会有这个:

void dummy(Dummy d, AsyncCallback< Dummy> callback);

GWT编译器将会捕捉到这个,因为 Dummy 类引用了那些类型,它将会把它们包含在白名单中。

示例 Dummy 类:

public class Dummy implements IsSerializable {
    private java.sql.Date d;
}

11

如果您在服务接口中包含了具体类型,以及它们引用的任何类型,只要它们实现了java.io.Serializable接口,这些类型就会自动被列入白名单。

public String getStringForDates(ArrayList<java.util.Date> dates);

会导致ArrayList和Date两者都被包含在白名单中。

如果尝试使用java.lang.Object而不是特定类型,则会更加棘手:

public Object getObjectForString(String str);

因为编译器不知道要纳入白名单的内容。在这种情况下,如果对象在您的服务接口中没有被任何地方引用,您必须使用IsSerializable接口显式标记它们,否则它将不允许您通过RPC机制传递它们。


我在使用java.lang.Double时遇到了错误,因此存在明显问题。 - Tomáš Zato
我在java.lang.Double和其他无法编辑的类型中遇到了错误。那么我该如何为它们添加IsSerializable?你能提供一个例子吗? - Tomáš Zato

8
白名单是由GWT编译器生成的,其中包含所有被指定为IsSerializable标记接口的条目。
要将类型添加到列表中,只需确保该类实现了IsSerializable接口。
此外,为了使序列化正常工作,该类必须具有默认的无参构造函数(如果需要,构造函数可以是私有的)。另外,如果该类是内部类,则必须标记为静态。

11
需要补充一点——这个类还需要有一个公共的默认无参构造函数。仅实现IsSerializable接口是不够的。我在调试了一个小时后才发现这一点。 - Adrian Petrescu
2
@AdrianPetrescu 这并不完全正确。无参构造函数可以是私有的,但必须存在。此外,类本身不能是私有的,如果它是内部类,则必须是静态的。而且类不能具有 final 非静态字段。 - FrankPl

2

在我看来,编程访问白名单的最简单方法是创建一个类似于以下代码的类:

public class SerializableWhitelist implements IsSerializable {
    String[] dummy1;
    SomeOtherThingsIWishToSerialize dummy2;
}

然后将其包含在.client包中,并从RPC服务中引用(以便编译器对其进行分析)。

我找不到更好的方法来启用非参数化映射的传输,显然有时需要这样做才能创建更通用的服务...


1
白名单是由gwt编译器生成的,其中包含所有由IsSerializable标记接口指定的条目。 要将类型添加到列表中,您只需要确保该类实现了IsSerializable接口。 -- Andrej
这可能是最简单的解决方案。 唯一需要记住的是,您想要序列化的所有类都应该有“公共、无参数”的构造函数,并且(根据要求)应该为成员字段提供setter方法。

默认构造函数的可见性并不重要。您只需要一个任意可见性的零个参数的默认构造函数(甚至私有的也可以)。 - thomaux

1

为确保所需结果,请删除所有 war/<app>/gwt/*.gwt.rpc


0

我发现仅仅将它放在客户端包中或者在虚拟服务接口中使用是不够的,因为系统会将其优化掉。

我发现最简单的方法是创建一个继承自服务接口中已有类型的类,并将其放入客户端包中。没有其他需要做的了。

public class GWTSerializableTypes extends SomeTypeInServiceInterface implements IsSerializable {
    Long l;
    Double d;
    private GWTSerializableTypes() {}
}

0
对于任何有相同问题但找不到满意答案的人...
我正在使用GWT和GWTController,因为我正在使用Spring,我按照此消息中所述进行了修改。该消息解释了如何修改GrailsRemoteServiceServlet,但是GWTController以相同的方式调用RPC.decodeRequest()和RPC.encodeResponseForSuccess()。
这是我正在使用的GWTController的最终版本:
/**
 * Used to instantiate GWT server in Spring context.
 *
 * Original version from <a href="http://docs.google.com/Doc?docid=dw2zgx2_25492p5qxfq&hl=en">this tutorial</a>.
 * 
 * ...fixed to work as explained <a href="http://blog.js-development.com/2009/09/gwt-meets-spring.html">in this tutorial</a>.
 * 
 * ...and then fixed to use StandardSerializationPolicy as explained in
 * <a href="http://markmail.org/message/k5j2vni6yzcokjsw">this message</a> to allow
 * using Serializable instead of IsSerializable in model.
 */
public class GWTController extends RemoteServiceServlet implements Controller, ServletContextAware {

 // Instance fields

 private RemoteService remoteService;

 private Class<? extends RemoteService> remoteServiceClass;

 private ServletContext servletContext;

 // Public methods

 /**
  * Call GWT's RemoteService doPost() method and return null.
  * 
  * @param request
  *            The current HTTP request
  * @param response
  *            The current HTTP response
  * @return A ModelAndView to render, or null if handled directly
  * @throws Exception
  *             In case of errors
  */
 public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
  doPost(request, response);
  return null; // response handled by GWT RPC over XmlHttpRequest
 }

 /**
  * Process the RPC request encoded into the payload string and return a string that encodes either the method return
  * or an exception thrown by it.
  * 
  * @param payload
  *            The RPC payload
  */
 public String processCall(String payload) throws SerializationException {
  try {
   RPCRequest rpcRequest = RPC.decodeRequest(payload, this.remoteServiceClass, this);

   // delegate work to the spring injected service
   return RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest.getMethod(), rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());
  } catch (IncompatibleRemoteServiceException e) {
   return RPC.encodeResponseForFailure(null, e);
  }
 }

 /**
  * Setter for Spring injection of the GWT RemoteService object.
  * 
  * @param RemoteService
  *            The GWT RemoteService implementation that will be delegated to by the {@code GWTController}.
  */
 public void setRemoteService(RemoteService remoteService) {
  this.remoteService = remoteService;
  this.remoteServiceClass = this.remoteService.getClass();
 }

 @Override
 public ServletContext getServletContext() {
  return servletContext;
 }

 public void setServletContext(ServletContext servletContext) {
  this.servletContext = servletContext;
 }
}

0

我曾经遇到过这个问题,但最终追溯到我的Serializable对象中的一行代码是问题所在:

Logger.getLogger(this.getClass().getCanonicalName()).log(Level.INFO, "Foo");

在异常被捕获之前,没有其他的投诉:

 @Override
  protected void serialize(Object instance, String typeSignature)
      throws SerializationException {
    assert (instance != null);

    Class<?> clazz = getClassForSerialization(instance);

    try {
      serializationPolicy.validateSerialize(clazz);
    } catch (SerializationException e) {
      throw new SerializationException(e.getMessage() + ": instance = " + instance);
    }
    serializeImpl(instance, clazz);
  }

而堆栈跟踪的业务端是:

com.google.gwt.user.client.rpc.SerializationException: Type 'net.your.class' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = net.your.class@9c7edce
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:619)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接