对于任何一个应用而言,客户端做的数据有效性验证都不是安全有效的,而数据验证又是一个企业级项目架构上最为基础的功能模块,这时候就要求我们在服务端接收到数据的时候也对数据的有效性进行验证。为什么这么说呢?往往我们在编写程序的时候都会感觉后台的验证无关紧要,毕竟客户端已经做过验证了,后端没必要在浪费资源对数据进行验证了,但恰恰是这种思维最为容易被别人钻空子。毕竟只要有点开发经验的都知道,我们完全可以模拟 HTTP 请求到后台地址,模拟请求过程中发送一些涉及系统安全的数据到后台,后果可想而知....

验证分两种:对封装的Bean进行验证  或者  对方法简单参数的验证。

说明:SpringBoot 中使用了 Hibernate-validate 校验框架作为支持

1. 创建项目Maven Project,修改pom.xml

<properties>
        <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
        <!-- jstl -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
    </dependency>
    <!-- jasper:jsp引擎 -->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

2. 在src/main/resources目录下新建application.properies,并添加全局配置

#jsp视图映射配置
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

3. 创建实体类并添加校验注解

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import org.hibernate.validator.constraints.Length;

public class User {

    private Integer userId;
    @NotBlank
    @Length(min=2,max=10)
    private String userName;
    @NotBlank
    private String password;
    private Integer age;
    @Email
    private String email;
    public User() {

    }
    public User(Integer userId, String userName, String password, Integer age, String email) {
        this.userId = userId;
        this.userName = userName;
        this.password = password;
        this.age = age;
        this.email = email;
    }
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setEmail(String email) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }
}

4. 编写Controller

import javax.validation.Valid;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * 页面跳转
     */
    @RequestMapping("/{page}")
    public Object showPage(Model model,@PathVariable String page, User user){
        ModelAndView view = new ModelAndView();
        view.addObject(model);
        view.setViewName(page);
        return view;
    }

    /*
     * @Validated 开启对 User对象的数据校验 (User对象需要添加注解)
     * BindingResult:封装了校验的结果,会自动添加到model对象进行传递
     * 注意:@Validated 和 BindingResult 是一一对应的,如果有多个@Valid,那么每个@Validated后面跟着的BindingResult就是这个@Validated的验证结果,顺序不能乱
     */
    @RequestMapping("/addUser")
    public Object addUser(Model model, @Validated User user, BindingResult result){
        ModelAndView view = new ModelAndView();
        if(result.hasErrors()){
            List<ObjectError> allErrors = result.getAllErrors();
            for(ObjectError error : allErrors){
                FieldError fieldError = (FieldError)error;
                // 属性
                String field = fieldError.getField();
                // 错误信息
                String message = fieldError.getDefaultMessage();
                System.out.println(field + ":" + message);
                //model的错误信息暂时不知怎么获取对应的消息,所以解析BindingResult后,单独放入model,在页面再获取
                model.addAttribute(field, message);

            }
            view.addObject(model);
            view.setViewName("input");
            return view;
        }
        view.setViewName("ok");
        return view;
    }
}

5. 在src/main目录下创建webapp/WEB-INF/jsp目录,在jsp文件夹下编写视图jsp

(1) 表单输入页面:input.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>添加用户</title>
</head>
<body>
    <h3>添加用户:</h3>
    <form action="/user/addUser" method="post">
        用户姓名:<input type="text" name="userName" /><font color="red">${userName }</font><br/>
        用户密码:<input type="password" name="password" /><font color="red">${password }</font><br/>
        用户年龄:<input type="text" name="age" /><font color="red">${age }</font><br/>
        用户邮箱:<input type="text" name="email" /><font color="red">${email }</font><br/>
        <input type="submit" value="确定" /><br />
    </form>
</body>
</html>

(2) 操作成功页面:ok.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>操作成功</title>
</head>
<body>
    <h5>操作 成功</h5>
</body>
</html>

6. 自定义错误信息提示

注:此处也可不在配置文件中配置,直接在验证的massage中写。

(1) 在resources 目录下新建提示信息配置文件ValidationMessages.properties,文件名是固定的,因为SpringBoot自动读取classpath中的ValidationMessages.properties里的错误信息。

ValidationMessages.properties 文件的编码为ASCII,格式为 key=value 。

#用户名不能为空
user.userName.notBlank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
#密码不能为空
user.password.notBlank=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A

(2) 修改实体类

private Integer userId;
@NotBlank(message="{user.userName.notBlank}")
@Length(min=2,max=10)
private String userName;
@NotBlank(message="{user.password.notBlank}")
private String password;
private Integer age;
@Email
private String email;

7. 方法参数中简单类型的校验

(1) 在Controller类上添加@Validated注解,只有添加这个,方法参数中的@Email等注解才能起作用

(2) 在Controller的方法参数前加上注解,可以加多个

@RequestMapping("/addUser2")
    public Object addUser(@NotBlank(message = "name 不能为空") @Length(min = 2, max = 10, message = "name 长度必须在 {min} - {max} 之间")String userName){
        ModelAndView view = new ModelAndView();
        view.setViewName("ok");
        return view;
    }

以上方法会抛出ConstraintViolationException异常:

javax.validation.ConstraintViolationException: addUser.userName: name 不能为空, addUser.userName: name 长度必须在 2 - 10 之间

无论是对象校验还是简单类型校验,如果存在需要校验多个,就会很麻烦,可用异常的方式进行统一处理,后续再讲。

8. 常用的Validation注解

@NotNull			值不能为空
@Null				值必须为空
@Pattern(regex=)	字符串必须匹配正则表达式
@Size(min, max)		集合元素的数量必须在min和max之间
@CreditCardNumber(ignoreNonDigitCharacters=)	字符串必须是信用卡号,按照美国的标准验证
@Email				字符串必须是Email地址
@Length(min, max)	检查字符串的长度
@NotBlank			字符串不能为空串(去掉首尾空格)
@NotEmpty			字符串不能为null, 集合必须有元素
@Range(min, max)	数字必须大于min, 小于max
@SafeHtml			字符串必须是安全的html
@URL				字符串必须是合法的URL
@AssertFalse		值必须是false
@AssertTrue			值必须是true
@DecimalMax(value=, inclusive=)	值必须小于等于(inclusive=true)/小于(inclusive=false)属性指定的值,也可以注释在字符串类型的属性上。
@DecimalMin(value=, inclusive=)	值必须大于等于(inclusive=true)/小于(inclusive=false)属性指定的值,也可以注释在字符串类型的属性上。
@Digist(integer=,fraction=)	数字格式检查。integer指定整数部分的最大长度,fraction指定小数部分的最大长度
@Future				时间必须是未来的
@Past				时间必须是过去的
@Max(value=)		值必须小于等于value指定的值。不能注解在字符串类型属性上。
@Min(value=)		值必须小于等于value指定的值。不能注解在字符串类型属性上。

9. 总结

需要注意的:

(1) 对象的校验,错误信息在jsp页面如何获取?

(2) 方法参数中简单类型的校验,Controller类上必须添加@Validated注解。

(3) 自定义校验的提示信息,ValidationMessages.properties的名称及文件的编码

【使用篇二】SpringBoot服务端数据校验(8)的更多相关文章

  1. 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

    前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...

  2. SpringBoot入门 (十一) 数据校验

    本文记录学习在SpringBoot中做数据校验. 一 什么是数据校验 数据校验就是在应用程序中,对输入进来得数据做语义分析判断,阻挡不符合规则得数据,放行符合规则得数据,以确保被保存得数据符合我们得数 ...

  3. [SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端

    原文:[SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端 之前开发基于WinForm监控的软件,服务端基于Wcf实现,里面涉及双工模式,在客户端里面,采用心跳包机制保持与服 ...

  4. 网站的优化----首页优化---app调取服务端数据

    高并发经常会发生在有大活跃用户量来访问网站的某个点,例如用户高聚集的业务场景中,如:抢购,促销等.为了让用户流畅的访问网站,来根据自己的业务设计适合系统的处理方案. //对于APP网站首页数据,通常是 ...

  5. android菜鸟学习笔记24----与服务器端交互(一)使用HttpURLConnection和HttpClient请求服务端数据

    主要是基于HTTP协议与服务端进行交互. 涉及到的类和接口有:URL.HttpURLConnection.HttpClient等 URL: 使用一个String类型的url构造一个URL对象,如: U ...

  6. python的flex服务端数据接口开发

    python的flex服务端数据接口开发 python 如果给flex提供服务端,需要提供一个网关和一个可供客户端(flex)调用的类.这方面我更加推荐用twisted来写这个网关,因为twisted ...

  7. android菜鸟学习笔记25----与服务器端交互(二)解析服务端返回的json数据及使用一个开源组件请求服务端数据

    补充:关于PHP服务端可能出现的问题: 如果你刚好也像我一样,用php实现的服务端程序,采用的是apache服务器,那么虚拟主机的配置可能会影响到android应用的调试!! 在android应用中访 ...

  8. Spring Boot2 系列教程 (十五) | 服务端参数校验之一

    估计很多朋友都认为参数校验是客户端的职责,不关服务端的事.其实这是错误的,学过 Web 安全的都知道,客户端的验证只是第一道关卡.它的参数验证并不是安全的,一旦被有心人抓到可乘之机,他就可以有各种方法 ...

  9. 手写MQ框架(二)-服务端实现

    一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...

随机推荐

  1. <DP> (高频)139 375 374 (DP hard)312

    139. Word Break 返回结果较为简单可用dp, 复杂用dfs class Solution { public boolean wordBreak(String s, List<Str ...

  2. vue 使用JavaScript表达式

    vue使用JavaScript的运算方式 代码如下: <!doctype html> <html lang="en"> <head> <m ...

  3. pycharm配置docker

  4. BFS(四):搜索状态判重

    在采用广度优先算法进行搜索时,一个需要重点注意的是在搜索过程中判重和去重.前面介绍的几个例子中,判重都较简单,如采用vis[]数组,若vis[i]==0,则i未访问过,i入队列:若vis[i]!=0, ...

  5. A query was run and no Result Maps were found for the Mapped Statement

    mybatis测试方法报错: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exec ...

  6. NoNodeAvailableException[None of the configured nodes are available:[.127.0.0.1}{127.0.0.1:9300]

    我在springboot 集成 elasticsearch,启动springboot测试创建索引,建立索引的时候报 : NoNodeAvailableException[None of the con ...

  7. 【UOJ#48】【UR #3】核聚变反应强度(质因数分解)

    [UOJ#48][UR #3]核聚变反应强度(质因数分解) 题面 UOJ 题解 答案一定是\(gcd\)除掉\(gcd\)的最小质因子. 而\(gcd\)的最小值因子一定是\(a_1\)的质因子. 所 ...

  8. 《数据结构》《C++程序设计》《计算机组成原理》中的英语名词

    一.数据结构 data 数据data element 数据元素data item 数据项data object 数据对象data structure 数据结构ADT (Abstruct Date Ty ...

  9. CefSharp F12打开DevTools查看console js和c#方法互相调用

    转载地址: https://www.cnblogs.com/lonelyxmas/p/11010018.html winform嵌入chrome浏览器,修改项目属性 生成 平台为x86 1.nuget ...

  10. SocketServer模块与简单并发服务器

    思维导图文件:https://files-cdn.cnblogs.com/files/benjieming/SocketServer%E6%A8%A1%E5%9D%97%E4%B8%8E%E7%AE% ...