如何实现REST资源的输入验证
如何实现REST资源的输入验证
我正在使用的SaaS平台具有一个RESTful接口,该接口可以接受XML有效负载。
实施REST资源
对于像我们这样的Java商店,使用JAX-B从XML Schema生成JavaBean类是有意义的。 在像Jersey的JAX-RS环境中,使用JAX-B处理XML(和JSON)有效负载非常容易。
@Path("orders")
public class OrdersResource {@POST@Consumes({ "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 Order's 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 {@XmlElement@Min(1)public int quantity;
}
@Path("orders")
public class OrdersResource {@POST@Consumes({ "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}")@PUT@Consumes("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}")@PUT@Consumes("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 ExceptionMapper<Throwable> {@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服务中处理输入验证? 您如何看待域驱动的安全性? 请发表评论。
翻译自: https://www.javacodegeeks.com/2013/08/how-to-implement-input-validation-for-rest-resources.html
如何实现REST资源的输入验证相关推荐
- rest 验证demo_如何实现REST资源的输入验证
rest 验证demo 如何实现REST资源的输入验证 我正在使用的SaaS平台具有一个RESTful接口,该接口可以接受XML有效负载. 实施REST资源 对于像我们这样的Java商店,使用JAX- ...
- Struts2用户输入验证(6)
10.6 小结 Struts2框架下的用户输入验证分为三种方式:1. 编程方式下的验证 2. 配置方式下的验证 3.注解方式下的验证. 如果在Struts2项目下存在多种验证方式,其 ...
- 3.Struts2的输入验证
l 当类型转换成功以后,struts2将进行输入验证 若要进行输入验证则你的action必须继承ActionSupport类,实现其validate方法 在方法中调用addFiled ...
- Struts2的输入验证(三)-短路验证与非字段验证
一.短路验证 若对一个字段使用多个验证器,默认情况下会执行所有的验证.若希望前面的验证器验证没有通过,后面的就不再验证,可以使用短路验证. 1)对同一个字段内的多个验证器,如果一个短路验证器验证失败, ...
- jquery中输入验证中一个不错的效果
在表单的输入验证中,经常要当用户没能正确输入后,要提示"XXXX输入错误" 这一类的信息,如何能搞到动态一点呢,今天发现jquery中的一个不错的效果,笔记之. 1 包含jquer ...
- C# 文本输入限制类型,datagridview单元格输入验证
1.只能输入double类型 private void textBoxX6_KeyPress(object sender, KeyPressEventArgs e){{//数字0~9所对应的keych ...
- C++为什么空格无法输出_C 语言 第8章-字符输入/输出和输入验证
1. 缓存区 #include 大部分系统在用户按下 Enter 键之前不会重复打印刚输入的字符,这种输入叫缓冲输入.用户输入的字符被收集并存储在缓冲区(buffer). 把若干字符作为一个块进行传输 ...
- Samba nsswitch/pam_winbind.c文件输入验证漏洞
漏洞名称: Samba nsswitch/pam_winbind.c文件输入验证漏洞 CNNVD编号: CNNVD-201312-047 发布时间: 2013-12-05 更新时间: 2013-12- ...
- ssh密码长度漏洞java_Linux中 OpenSSH 输入验证错误漏洞(CVE-2019-16905) 修复解决方案...
解决方案: redhat6.7与centos6.7版本升级到openssh8.1版本与openssl-1.1.1自动化脚本,解决linuxOpenSSH输入验证错误漏洞(CVE-2019-16905) ...
最新文章
- linux 创建虚拟IP
- 【工具】13 款 Linux 实用工具推荐,个个是神器!(附下载链接)
- HDU - 3518 Boring counting(后缀数组)
- call / apply / bind
- Go编程技巧--io.Reader/Writer
- ROS调用ORB-SLAM2
- 【剑指 offer】(十三)—— 在 O(1) 时间删除链表结点
- 序列化和反序列化(三)——父类的序列化
- 可编程接口芯片8255A
- uniapp:微信小程序地图功能
- Gensim库的使用——Word2vec模型(二)训练自己的模型与训练参数
- ios学习路线图_iOS开发学习路线 +技巧整理
- 软件测试 | 测试开发 | Git分支管理搞定在线合并和本地合并
- 什么是802.11ac和802.11ac Wave2
- 人造肌肉——双扭绳驱动结构
- 腾讯地图api-基本用法总结
- python生成渐变颜色数组
- SpringMVC使用HandlerExceptionResolver实现全局异常处理器
- (新手)使用sklearn对Indian_pines_corrected高光谱遥感数据集的简单特征选择与分类
- 女子上班10天被辞退获赔1万,不料竟收到122斤硬币,法院做法亮了
热门文章
- 作为 IT 行业的过来人,你有什么话想对后辈说的?2
- Java Web开发与实战_Java Web开发技术与实战项目
- springboot-Initializer例子及分析
- 报错:The type javax.servlet.http.HttpServletRequest cannot be resolved
- tomcat(2)一个简单的servlet容器
- DFS应用——找出无向图的割点
- envoy api 网关_为Envoy构建控制平面的指南-特定于域的配置API
- ljc.framework_Java 9模块系统(拼图)@ LJC的HackTheTower
- kata_小规模流处理kata。 第2部分:RxJava 1.x / 2.x
- java 多线程变量可见性_Java多线程:易变变量,事前关联和内存一致性