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

发布于 2024-07-06 21:57:59 字数 478 浏览 13 评论 0 原文

GWT 的序列化程序对 java.io.Serialized 支持有限,但出于安全原因,它支持的类型有一个白名单。 我找到的文档,例如此常见问题解答条目 表示您想要序列化的任何类型“必须包含在序列化策略白名单中”,并且该列表是在编译时生成的,但没有解释编译器如何决定白名单上的内容。

生成的列表包含许多属于标准库的类型,例如 java.lang.String 和 java.util.HashMap。 我在尝试序列化 java.sql.Date 时遇到错误,它实现了 Serialized 接口,但不在白名单中。 如何将此类型添加到列表中?

GWT's serializer has limited java.io.Serializable support, but for security reasons there is a whitelist of types it supports. The documentation I've found, for example this FAQ entry says that any types you want to serialize "must be included in the serialization policy whitelist", and that the list is generated at compile time, but doesn't explain how the compiler decides what goes on the whitelist.

The generated list contains a number of types that are part of the standard library, such as java.lang.String and java.util.HashMap. I get an error when trying to serialize java.sql.Date, which implements the Serializable interface, but is not on the whitelist. How can I add this type to the list?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

紫南 2024-07-13 21:57:59

有一个解决方法:定义一个新的 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;
}

There's a workaround: define a new Dummy class with member fields of all the types that you want to be included in serialization. Then add a method to your RPC interface:

Dummy dummy(Dummy d);

The implementation is just this:

Dummy dummy(Dummy d) { return d; }

And the async interface will have this:

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

The GWT compiler will pick this up, and because the Dummy class references those types, it will include them in the white list.

Example Dummy class:

public class Dummy implements IsSerializable {
    private java.sql.Date d;
}
梦幻的味道 2024-07-13 21:57:59

您在服务接口中包含的任何特定类型以及它们引用的任何类型都将自动列入白名单,只要它们实现 java.io.Serialized,例如:

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

将导致 ArrayList 和 Date 都包含在白名单中。

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

public Object getObjectForString(String str);

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

Any specific types that you include in your service interface and any types that they reference will be automatically whitelisted, as long as they implement java.io.Serializable, eg:

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

Will result in ArrayList and Date both being included on the whitelist.

It gets trickier if you try and use java.lang.Object instead of specific types:

public Object getObjectForString(String str);

Because the compiler doesn't know what to whitelist. In that case if the objects are not referenced anywhere in your service interface, you have to mark them explicitly with the IsSerializable interface, otherwise it won't let you pass them through the RPC mechanism.

献世佛 2024-07-13 21:57:59

白名单由 GWT 编译器生成,包含由 IsSerialized 标记接口指定的所有条目。

要将类型添加到列表中,您只需确保该类实现 IsSerializable 接口。

此外,为了使序列化正常工作,类必须有一个默认的无参数构造函数(如果需要,构造函数可以是私有的)。 此外,如果该类是内部类,则必须将其标记为静态。

The whitelist is generated by the GWT compiler and contains all the entries that are designated by the IsSerializable marker interface.

To add a type to the list you just need to make sure that the class implements the IsSerializable interface.

Additionally for serialization to work correctly the class must have a default no arg constructor (constructor can be private if needed). Also if the class is an inner it must be marked as static.

薄荷梦 2024-07-13 21:57:59

恕我直言,以编程方式访问白名单的最简单方法是创建一个与此类似的类:

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

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

我找不到更好的方法来启用非参数化地图的传输,这显然是您有时需要创建更通用的服务......

IMHO the simpliest way to access whitelist programmatically is to create a class similar to this:

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

Then include it in the .client package and reference from the RPC service (so it gets analyzed by the compiler).

I couldn't find a better way to enable tranfer of unparameterized maps, which is obviously what you sometimes need in order to create more generic services...

千鲤 2024-07-13 21:57:59

白名单由 gwt 编译器生成,包含 IsSerialized 标记接口指定的所有条目。

要将类型添加到列表中,您只需确保该类实现 IsSerialized 接口。

--安德烈

这可能是最简单的解决方案。
唯一要记住的是,您想要序列化的所有类都应该具有“公共、无参数”构造函数,以及(根据要求)成员字段的 setter 方法。

The whitelist is generated by the gwt compiler and contains all the entries that are designated by the IsSerializable marker interface.

To add a type to the list you just need to make sure that the class implements the IsSerializable interface.

-- Andrej

This is probably the easiest solution.
The only thing to remember with this is that all the classes that you want to serialize should have "public, no-argument" constructor, and (depending upon requirements) setter methods for the member fields.

无所谓啦 2024-07-13 21:57:59

为了确保获得所需的结果,请删除所有 war//gwt/*.gwt.rpc

to ensure the desired result delete all war/<app>/gwt/*.gwt.rpc

变身佩奇 2024-07-13 21:57:59

对于任何有同样问题并且没有找到令人满意的答案的人...

我将 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;
 }
}

To anyone who will have the same question and doesn't find previous answers satisfactory...

I'm using GWT with GWTController, since I'm using Spring, which I modified as described in this message. The message explains how to modify GrailsRemoteServiceServlet, but GWTController calls RPC.decodeRequest() and RPC.encodeResponseForSuccess() in the same way.

This is the final version of GWTController I'm using:

/**
 * 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;
 }
}
白昼 2024-07-13 21:57:59

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

我发现最简单的方法是创建一个派生自服务接口中已使用的类型之一的类,并将其粘贴到客户端包中。 不需要其他任何东西。

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

I found that just putting it in the client package or using it in a dummy service interface was not sufficient as it seemed the system optimized it away.

I found it easiest to create a class that derived from one of the types already used in the service interface and stick it in the client package. Nothing else needed.

public class GWTSerializableTypes extends SomeTypeInServiceInterface implements IsSerializable {
    Long l;
    Double d;
    private GWTSerializableTypes() {}
}
救星 2024-07-13 21:57:59

我遇到了这个问题,但最终将问题追溯到可序列化对象中的一行代码:

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)

I had this problem but ended up tracing the problem back to a line of code in my Serializable object:

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

There were no other complaints before the exception gets caught in:

 @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);
  }

And the business end of the stack trace is:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文