网站模板和源码区别,自学网站建设要多久,wordpress ico不显示不出来,百度地图人工服务如何实现REST资源的输入验证 我正在使用的SaaS平台具有一个RESTful接口#xff0c;该接口可以接受XML有效负载。 实施REST资源 对于像我们这样的Java商店#xff0c;使用JAX-B从XML Schema生成JavaBean类是有意义的。 在像Jersey的JAX-RS环境中#xff0c;使用JAX-B处理X… 如何实现REST资源的输入验证 我正在使用的SaaS平台具有一个RESTful接口该接口可以接受XML有效负载。 实施REST资源 对于像我们这样的Java商店使用JAX-B从XML Schema生成JavaBean类是有意义的。 在像Jersey的JAX-RS环境中使用JAX-B处理XML和JSON有效负载非常容易。 Path(orders)
public class OrdersResource {POSTConsumes({ application/xml, application/json })public void place(Order order) {// Jersey marshalls the XML payload into the Order // JavaBean, allowing us to write type-safe code // using Orders getters and setters.int quantity order.getQuantity();// ...}
} 请注意您不应该使用这些通用媒体类型但这是另一天的讨论。 本文的其余部分假定使用JAX-B但其要点也适用于其他技术。 无论您做什么都不要使用XMLDecoder 因为这对许多漏洞都是开放的 。 保护REST资源 假设订单的quantity用于结算并且我们想防止人们输入负数来窃取我们的钱 。 我们可以通过输入验证 AppSec工具箱中最重要的工具之一来做到这一点。 让我们看一下实现它的一些方法。 使用XML模式进行输入验证 我们可以依靠XML Schema进行验证 但是XML Schema只能验证那么多。 验证单个属性可能会很好但是当我们要验证属性之间的关系时事情变得很麻烦。 为了获得最大的灵活性我们希望使用Java来表达约束。 更重要的是 在REST服务中 模式验证通常不是一个好主意 。 REST的主要目标是使客户端和服务器脱钩以便它们可以分别发展。 如果我们根据模式进行验证则发送新属性的新客户端将与无法理解该新属性的旧服务器发生冲突。 通常最好静默忽略您不了解的属性。 JAX-B可以做到这一点反之亦然旧客户端未发送的属性最终为null 。 因此新服务器必须小心以正确处理null值。 使用Bean验证的输入验证 如果我们不能使用模式验证那么使用JSR 303 Bean验证又如何呢 Jersey通过将jersey-bean-validation jar添加到您的类路径来支持Bean验证。 有一个非官方的Maven插件可以将Bean验证注释添加到JAX-B生成的类中但是我宁愿使用更好的支持并且可以与Gradle一起使用 。 因此让我们扭转局势。 我们将手工制作JavaBean并从Bean生成XML Schema进行文档编制 XmlRootElement(name order)
public class Order {XmlElementMin(1)public int quantity;
}Path(orders)
public class OrdersResource {POSTConsumes({ application/xml, application/json })public void place(Valid Order order) {// Jersey recognizes the Valid annotation and// returns 400 when the JavaBean is not valid}
} 任何企图POST与非阳性数量的订单现在将给予400 Bad Request状态。 现在假设我们要允许客户更改其挂单。 我们将使用PATCH或PUT更新单个订单属性例如数量 Path(orders)
public class OrdersResource {Path({id})PUTConsumes(application/x-www-form-urlencoded)public Order update(PathParam(id) String id, Min(1) FormParam(quantity) int quantity) {// ...}
} 我们也需要在此处添加Min注释这是重复的。 为了使这个DRY 我们可以将quantity变成负责验证的类 Path(orders)
public class OrdersResource {Path({id})PUTConsumes(application/x-www-form-urlencoded)public Order update(PathParam(id) String id, FormParam(quantity)Quantity quantity) {// ...}
}XmlRootElement(name order)
public class Order {XmlElementpublic Quantity quantity;
}public class Quantity {private int value;public Quantity() { }public Quantity(String value) {try {setValue(Integer.parseInt(value));} catch (ValidationException e) {throw new IllegalArgumentException(e);}}public int getValue() {return value;}XmlValuepublic void setValue(int value) throws ValidationException {if (value 1) {throw new ValidationException(Quantity value must be positive, but is: value);}this.value value;}
} 我们需要JAX-B的公共no-arg构造函数以便能够将有效载荷解组到JavaBean中而另一个构造函数则使用String来使FormParam起作用。 setValue()抛出javax.xml.bind.ValidationException以便JAX-B将停止解组。 但是Jersey看到异常时会返回500 Internal Server Error 。 我们可以通过使用异常映射器将验证异常映射到400状态代码来解决此问题。 在此过程中让我们对IllegalArgumentException做同样的事情 Provider
public class DefaultExceptionMapper implements ExceptionMapperThrowable {Overridepublic Response toResponse(Throwable exception) {Throwable badRequestException getBadRequestException(exception);if (badRequestException ! null) {return Response.status(Status.BAD_REQUEST).entity(badRequestException.getMessage()).build();}if (exception instanceof WebApplicationException) {return ((WebApplicationException)exception).getResponse();}return Response.serverError().entity(exception.getMessage()).build();}private Throwable getBadRequestException(Throwable exception) {if (exception instanceof ValidationException) {return exception;}Throwable cause exception.getCause();if (cause ! null cause ! exception) {Throwable result getBadRequestException(cause);if (result ! null) {return result;}}if (exception instanceof IllegalArgumentException) {return exception;}if (exception instanceof BadRequestException) {return exception;}return null;}} 域对象的输入验证 即使上面概述的方法对于许多应用程序都可以很好地工作但从根本上来说还是有缺陷的。 乍一看 领域驱动设计 DDD的支持者可能喜欢创建“ Quantity类的想法。 但是“ Order和“ Quantity类不能为领域概念建模。 他们为REST表示建模。 这种区别可能很微妙但很重要。 DDD处理领域概念而REST处理这些概念的表示 。 发现了领域概念但是设计了表示形式并且需要进行各种折衷。 例如集合REST资源可以使用分页来防止通过网络发送太多数据。 另一个REST资源可能结合了多个域概念以使客户端-服务器协议的聊天性降低。 REST资源甚至可能根本没有对应的域概念。 例如一个POST可能返回202 Accepted并指向代表异步事务进度的REST资源。 域对象需要尽可能接近地捕获普遍存在的语言 并且必须权衡利弊才能使功能起作用。 另一方面在设计REST资源时需要权衡满足非功能性需求例如性能可伸缩性和可扩展性。 这就是为什么我认为像RESTful Objects这样的方法不起作用的原因。 出于类似原因我不相信UI的Naked Objects 。 在我们的资源表示形式的JavaBeans中添加验证意味着这些bean现在有两个更改的原因这明显违反了“ 单一职责原则” 。 当仅将JAX-B JavaBeans用于REST表示并创建处理验证的单独域对象时我们得到的架构会更简洁。 将验证放在域对象中是Dan Bergh Johnsson所谓的“ 域驱动的安全性” 。 在这种方法中原始类型被值对象替代。 甚至有人反对使用任何String 。 起初创建一个用于容纳单个整数的全新类似乎有些矫kill过正但是我敦促您尝试一下。 您可能会发现摆脱原始的迷恋甚至可以提供超出验证的价值。 你怎么看 您如何在RESTful服务中处理输入验证 您如何看待域驱动的安全性 请发表评论。 参考 如何从安全软件开发博客上的JCG合作伙伴 Remon Sinnema 获得REST资源的输入验证 。 翻译自: https://www.javacodegeeks.com/2013/08/how-to-implement-input-validation-for-rest-resources.html