SpringBoot 實戰 (十五) | 服務端引數校驗之一

一個優秀的廢人發表於2019-03-03

前言

估計很多朋友都認為引數校驗是客戶端的職責,不關服務端的事。其實這是錯誤的,學過 Web 安全的都知道,客戶端的驗證只是第一道關卡。它的引數驗證並不是安全的,一旦被有心人抓到可乘之機,他就可以有各種方法來摸擬系統的 Http 請求,訪問資料庫的關鍵資料。輕則導致伺服器當機,重則洩露資料。所以,這時就需要設定第二道關卡,服務端驗證了。

老專案的服務端校驗

@RestController
@RequestMapping("/student")
public class ValidateOneController {

    @GetMapping("/id")
    public Student findStudentById(Integer id){
        if(id == null){
              logger.error("id 不能為空!");
              throw new NullPointerException("id 不能為空");
        }
        return studentService.findStudentById(id);
    }
}
複製程式碼

看以上程式碼,就一個的校驗就如此麻煩。那我們是否有好的統一校驗方法呢?鑑於 SpringBoot 無所不能。答案當然是有的。

其中,Bean Validator 和 Hibernate Validator 就是兩套用於驗證的框架,二者都遵循 JSR-303 ,可以混著用,鑑於二者的某些 Validator 註解有差別,例如 @Length 在 Bean Validator 中是沒有的,所以這裡我選擇混合用。

JSR-303

JSR-303 是JAVA EE 6 中的一項子規範,叫做 Bean Validation,Hibernate Validator 是 Bean Validation 的參考實現, Hibernate Validator 提供了 JSR 303 規範中所有內建 Constraint(約束) 的實現,除此之外還有一些附加的 Constraint 。這些 Constraint (約束) 全都通過註解的方式實現,請看下面兩個表。

Bean Validation 中內建的約束:

註解 作用
@Null 被註解引數必須為空
@NotNull 被註解引數不能為空
@AssertTrue 被註解引數必須為 True
@AssertFalse 被註解引數必須為 False
@Min(value) 被註解引數必須是數字,且其值必須大於等於 value
@Max(value) 被註解引數必須是數字,且其值必須小於等於 value
@DecimaMin(value) 被註解引數必須是數字,且其值必須大於等於 value
@DecimaMax(value) 被註解引數必須是數字,且其值必須小於等於 value
@Size(max, min) 被註解引數大小必須在指定範圍內
@Past 被註解引數必須是一個過去的日期
@Future 被註解引數必須是一個將來的日期
@Pattern(value) 被註解引數必須符合指定的正規表示式
@Digits(integer, fraction) 被註解引數必須是數字,且其值必須在可接受範圍內
@NotBlank 被註解引數的值不為空(不為 null、去除首位空格後長度為 0),不同於 @NotEmpty,@NotBlank 只應用於字串且在比較時會去除字串的空格

Hibernate Validator 附加的約束:

註解 作用
@NotEmpty 被註解引數的值不為 null 且不為空(字串長度不為0、集合大小不為0)
@Email 被註解引數必須是電子郵箱地址
@Length 被註解的字串長度必須在指定範圍內
@Range 被註解的引數必須在指定範圍內

準備工作

  • SpringBoot 2.1.3
  • IDEA
  • JDK8

Pom 檔案依賴

<!-- web 啟動類 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- test 單元測試類 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!-- lombok 依賴用於簡化 bean -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
複製程式碼

實體類

用於測試,加入了引數校驗規則。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private Integer id;

    @NotBlank(message = "學生名字不能為空")
    @Length(min = 2, max = 10, message = "name 長度必須在 {min} - {max} 之間")
    private String name;

    @NotNull(message = "年齡不允許為空")
    @Min(value = 0, message = "年齡不能低於 {value} 歲")
    private Integer age;
}
複製程式碼

Controller 層

寫了兩個方法,一個用於校驗普通引數,一個用於校驗物件

@Validated //開啟資料校驗,新增在類上用於校驗方法,新增在方法引數中用於校驗引數物件。(新增在方法上無效)
@RestController
@RequestMapping("/student")
public class ValidateOneController {

    /**
     * 普通引數校驗
     * @param name
     * @return
     */
    @GetMapping("/name")
    public String findStudentByName(@NotBlank(message = "學生名字不能為空")
    @Length(min = 2, max = 10, message = "name 長度必須在 {min} - {max} 之間")String name){
        return "success";
    }

    /**
     * 物件校驗
     * @param student
     * @return
     */
    @PostMapping("/add")
    public String addStudent(@Validated @RequestBody Student student){
        return "success";
    }
}
複製程式碼

Postman 測試

校驗普通引數測試結果:

下圖可以看見,我沒有在 http://localhost:8080/student/name 地址後新增 name 引數,傳到後臺馬上就校驗出異常了。而這個異常資訊就是我定義的校驗異常資訊。

校驗普通引數測試結果

校驗物件測試結果:

校驗物件測試截圖

結果有點長:

下圖可以看見,我訪問 http://localhost:8080/student/add 傳入了引數物件,但物件是不能通過校驗規則的,比如 age 引數為負數,name 引數長度太大,傳到後臺馬上就校驗出異常了。而這個異常資訊就是我定義的校驗異常資訊。

校驗物件測試結果

完整程式碼

https://github.com/turoDog/Demo/tree/master/springboot_validateone_demo

如果覺得對你有幫助,請給個 Star 再走唄,非常感謝。

後語

如果本文對你哪怕有一丁點幫助,請幫忙點好看。你的好看是我堅持寫作的動力。

另外,關注之後在傳送 1024 可領取免費學習資料。

資料詳情請看這篇舊文:Python、C++、Java、Linux、Go、前端、演算法資料分享

一個優秀的廢人

相關文章