rest 验证demo_如何实现REST资源的输入验证
rest 驗證demo
如何實現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模式以進行文檔編制:
@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 驗證demo
總結
以上是生活随笔為你收集整理的rest 验证demo_如何实现REST资源的输入验证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果7p相机设置(苹果7p相机设置技巧)
- 下一篇: 电脑赫兹CPU是什么(赫兹是cpu的)