API Safety - parameter verification

1. Why data verification

To ensure the security and robustness of the system, data verification is essential to verify the legitimacy of parameters. We can't report errors in our system because the front end or other call segments have incorrect parameters.

2. Where is parameter verification in development

Generally, it is used at the interface level to verify the incoming parameters.

3,Bean Validation

For the parameter verification of the Controller interface, if there are fewer parameters, you can write your own code for verification. However, if there are more parameters, a pile of if else will be used. The code is not beautiful. We can use Bean Validation to verify parameters. Bean Validation is a Java specification that allows you to use annotations to constrain fields on objects or customize constraints. From 1.0 (JSR303), 1.1 (JSR349) to 2.0 (JSR380). Using Bean Validation 2.0 requires Java 8 or higher, with Hibernate Validator as the provider.

4. Using Bean Validation in SpringBoot

If we use the spring boot starter web dependency, the dependency will be automatically added for us. If you want to use it alone, you need to add a spring boot starter validation dependency.

 

 

 

5. Validation annotation in Bean Validation

In the validation API, javax Validation A series of standard verification comments are provided under the constraints package

@AssertFalse: value must be false. The supported types are Boolean and Boolean. null values are considered legal.

@AssertTrue: the value must be true. The supported types are Boolean and Boolean. null values are considered legal.

@DecimalMax: the value must be less than or equal to the value specified by value; Whether to include value is controlled by inclusive. It defaults to include (true). The supported types are BigDecimal, BigInteger, string, byte, short, int, long and their wrapper classes. double and float are not supported. null values are considered legal.

@DecimalMin: the value must be greater than or equal to the value specified by value; Whether to include value is controlled by inclusive. It defaults to include (true). The supported types are BigDecimal, BigInteger, string, byte, short, int, long and their wrapper classes. double and float are not supported. null values are considered legal.

@Digits: must be a number within the specified range. Integer specifies the maximum length of the integer part; fraction specifies the maximum length of the decimal part. The supported types are BigDecimal, BigInteger, string, byte, short, int, long and their wrapper classes. null values are considered legal.

@email: the string must be a properly formatted email address. You can also specify a custom email format through regexp and flag. null values are considered legal.

@Future: must be a Future time or Date. The supported types are Date, Instant, LocalDate, LocalDateTime, LocalTime and other common time and Date classes. null values are considered legal.

@FutureOrPresent: must be a current or future time or Date. The supported types are Date, Instant, LocalDate, LocalDateTime, LocalTime and other common time and Date classes. null values are considered legal.

@Max: the value must be less than or equal to the specified value. The supported types are BigDecimal, BigInteger, byte, short, int, long and their wrapper classes. double and float are not supported. null values are considered legal.

@Min: the value must be greater than or equal to the specified value. The supported types are BigDecimal, BigInteger, byte, short, int, long and their wrapper classes. double and float are not supported. null values are considered legal.

@Negative: the value must be Negative (less than zero). The supported types are BigDecimal, BigInteger, byte, short, int, long, float, double and their wrapper classes. null values are considered legal.

@NegativeOrZero: value must be less than or equal to zero. The supported types are BigDecimal, BigInteger, byte, short, int, long, float, double and their wrapper classes. null values are considered legal.

@NotBlank: the string cannot be empty and must contain at least one non white space character. Illegal null value.

@NotEmpty: the value cannot be empty. Supported types, string cannot be null and length cannot be 0; Collection cannot be null and empty; Map cannot be null and empty; Array cannot be null and empty.

@NotNull: value cannot be null. Any type is supported.

@null: value must be null. Any type is supported.

@Past: must be a Past time or Date. The supported types are Date, Instant, LocalDate, LocalDateTime, LocalTime and other common time and Date classes. null values are considered legal.

@PastOrPresent: must be a current or past time or Date. The supported types are Date, Instant, LocalDate, LocalDateTime, LocalTime and other common time and Date classes. null values are considered legal.

@Pattern: must match the specified regular expression. null values are considered legal.

@Positive: the value must be a Positive number (greater than zero). The supported types are BigDecimal, BigInteger, byte, short, int, long, float, double and their wrapper classes. null values are considered legal.

@PositiveOrZero: value must be greater than or equal to zero. The supported types are BigDecimal, BigInteger, byte, short, int, long, float, double and their wrapper classes. null values are considered legal.

@Size: the number must be between the specified min and max (including min and max). The supported type string length, the number of elements in the collection, the number of key value pairs in the Map, and the length of the array. null values are considered legal.

In Hibernate validator, the Hibernate Validator A series of verification annotations are also provided under the constraints package. The common ones are as follows:

@Length: the string Length is between the specified min and max (including min and max). null values are considered legal.

@Range: the value must be within the appropriate Range min and max (including min and max). A string representation applied to a number or number. null values are considered legal. @url: whether the string is a URL. You can also specify a custom email format through regexp and flag. null values are considered legal.

6. Add user usage parameter verification

6.1. Add verification annotation on UserDTO

/**
 */
@Data
public class UserDTO {

    @Null(message = "When creating a user, id Must be null")
    private Long id;
    
    @NotNull(message = "Name cannot be empty")
    private String name;

    @NotBlank(message = "User name cannot be empty")
    private String username;

    @NotBlank(message = "Password cannot be empty")
    private String password;

}

6.2 add @Validated annotation on interface class

@Validated
@RestController
@RequestMapping("/users")
public class UserController {
......
}

6.3 add @Validated or @Valid annotation on the interface

    @PostMapping
    public UserDTO create(@RequestBody @Validated UserDTO userDTO){
        return userService.create(userDTO);
    }

6.4. Add a user to the test. At this time, our parameters will be verified. If they do not meet our requirements, an error will be reported

 

 

 

 

 

Correct parameters

 

 

7. Abnormal result handling 1

When the error parameter is accessed, you can know that an org Springframework Web Bind Methodargumentnotvalidexception exception,

 

 

We capture and process them to make the returned results more friendly.

@RestControllerAdvice
public class ControllerAdvice {


    /**
     * @param e Parameter verification exception
     * @return Specific non-conforming parameters and their causes
     */
    @ExceptionHandler
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public Map<String, String> exceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException e) {
        return e.getBindingResult().getFieldErrors().stream()
                .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
    }


}

  

Test the error parameters again, and the returned results will be more friendly

 

 

8. Group verification

To add a user, we do not need a user id, but to modify a user, we need a user id. How to perform verification at this time?

Bean Validation provides us with the grouping function. In fact, each validation annotation has a groups attribute, which is empty by Default (Default grouping).

8.1 Create two interfaces, Create and Update, and inherit the Default interface. (if you do not inherit, the Default group will not be verified)

/**
 *  New group
 */
public interface Create extends Default {
}

/**
 * Modify group
 * @author caofanqi
 * @date 2020/1/27 21:40
 */
public interface Update extends Default {
}

  

8.2. Modify the id annotation on the UserDTO and add a group for it

    @Null(groups = Create.class,message = "When creating a user, id Must be null")
    @NotNull(groups = Update.class,message = "When modifying a user, you must have id")
    private Long id;

 

8.3. Add a group on @Validated of the interface

@PostMapping
    public UserDTO create(@RequestBody @Validated(Create.class) UserDTO userDTO){
        return userService.create(userDTO);
    }

    @PutMapping
    public UserDTO update(@RequestBody @Validated(Update.class) UserDTO userDTO) {
        return userService.update(userDTO);
    }

  

8.4 test error parameter results are as follows

 

 

 

 

9. Cascading verification (attribute verification in object attributes)

If the attribute is an object, if we only add the @NotNull annotation, it will not verify the attribute in the object. If you want to verify, you need to add the @Valid annotation.

9.1. Force an example to put the mobile phone number into the detail object

/**
 * User details, in order to test cascade verification
 */
@Data
public class UserInfoDTO {

    @NotBlank(message = "Mobile number cannot be empty")
    @Pattern(regexp="^[1]([3-9])[0-9]{9}$",message="Incorrect mobile number format")
    private String phone;

}

  

9.2. Add the @Valid annotation to the UserDTO to verify the phone number

    @Valid
    @NotNull(message = "User details cannot be blank")
    private UserInfoDTO userInfo;

9.3 the tests are as follows

 

 

 

10. Custom annotation verification

If the verification annotation provided to us cannot meet our needs, we can customize the verification annotation.

10.1 create an annotation, find an original verification annotation to copy, modify @Constraint to specify the verification logic (multiple can be specified), and the required attributes.

/**
 * User defined verification annotation. The specific verification logic is specified by @Constraint. You can specify multiple
 *
 */
@Documented
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = {CustomConstraintValidatorForString.class, CustomConstraintValidatorForList.class})
public @interface CustomConstraint {

    String message() default "Custom verification annotation";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    int value() default 0;


}

  

10.2. Create a verifier, implement the ConstraintValidator interface, and specify the annotation to be verified and the type to be verified. initialize can obtain the attributes on the annotation for initialization; isValid validation logic.

/**
 * Customize the annotation verifier, implement the ConstraintValidator interface, and specify the annotation to be verified and the type to be verified,
 * initialize You can obtain the attributes on the annotation for initialization; isValid validation logic.
 *
 */
@Slf4j
public class CustomConstraintValidatorForString implements ConstraintValidator<CustomConstraint,String> {

    private int value;

    @Override
    public void initialize(CustomConstraint constraintAnnotation) {
        value = constraintAnnotation.value();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        log.info("The parameter values of user-defined verification are:{},The values on the annotation are:{}",value,this.value);
        return true;
    }


}

 

10.3 test. The console prints as follows, indicating that the verification logic is implemented

 

 

 

11. Group verification in list

If the batch add function is available, the entire list needs to be verified as follows

11.1. Add the @Validated(Create.class) annotation to the method

11.2 add @Valid annotation to the List

@PostMapping("/batch")
    @Validated(Create.class)
    public void batchCreate(@RequestBody  List<@Valid UserDTO> userDTOS){
        userService.batchCreate(userDTOS);
    }

11.3. Test wrong parameters, report errors, and the results are not friendly

 

 

12. Exception result handling 2

When accessing error parameters, you can know that a javax Validation The constraintviolationexception is caught and processed to make the returned result more friendly.

/**
     * @param e Parameter verification exception 2
     * @return Specific non-compliance paths and their causes
     */
    @ExceptionHandler
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public Map<Path, String> exceptionHandler(javax.validation.ConstraintViolationException e){
        return e.getConstraintViolations().stream()
                .collect(Collectors.toMap(ConstraintViolation::getPropertyPath, ConstraintViolation::getMessage));
    }

Test the error parameters again, and the returned results will be more friendly

 

 

13. Association verification between attributes

Here, we can customize a class verification annotation, so that we can get all the attributes and verify them. For example, name cannot be the same as username.

13.1 user defined class annotation

/**
 * Custom class level annotation
 */
@Documented
@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = {UserDTOConstraintValidator.class})
public @interface UserDTOConstraint {

    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

  

13.2 calibrator

/**
 * Custom UserDTO class validator
 *
 */
@Slf4j
public class UserDTOConstraintValidator implements ConstraintValidator<UserDTOConstraint, UserDTO> {


    @Override
    public boolean isValid(UserDTO value, ConstraintValidatorContext context) {

        if (value.getName().equals(value.getUsername())){
            /*
             * Change error message
             */
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("The name cannot be the same as the user name")
                    .addPropertyNode("name").addConstraintViolation();

            return false;
        }

        return true;
    }
}

  

13.3. Add the annotation on the class

@Data
@UserDTOConstraint(message = "default message")
public class UserDTO {
......
}

13.4 testing

 

 

Bean Validation official website: https://beanvalidation.org/

Hibernate Validator official reference document: https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#preface

 

 

 

Tags: Cyber Security

Posted by flyingeagle855 on Mon, 30 May 2022 15:11:11 +0530