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

    题意 大力猜结论. 首先将所有\(a_i\)变为\(a_i-i\),之后求不严格递增的\(b_i\),显然答案不变,最后\(b_i\)加上\(i\)即可. 考虑两种特殊情况: 1.\(a[]\)是递增 ...

  2. oracle序列相关

    一. oracle中如何实现一列的规律增长呢(通常是指number类型的列)? 这就需要借助序列来实现了; 1. 什么是序列? 可以理解为序列是一组sql语法创建出来的函数, 该函数中定义     好 ...

  3. SpringCloud微服务常见组件理解

    概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...

  4. github上计算String相似度好的项目

    项目中包含了杰卡德NGram.cosin夹角.最长公共子序列.边际距离等常用的相似度算法. https://github.com/tdebatty/java-string-similarity

  5. Qt 删掉资源qss后报错

    Error: dependent '..\..\........XXXX.qss' does not exist. 解决方案: 1.清理工程 2.qmake 3.重新构建

  6. k8s云集群混搭模式落地分享

    在 <k8s云集群混搭模式,可能帮你节省50%以上的服务成本>一文中,介绍了使用k8s + 虚拟节点混合集群的方式,为负载具有时间段波峰.波谷交替规律的业务节约成本,提高服务伸缩效率的部署 ...

  7. 《Linux内核原理与分析》教学进程

    目录 2019-2020-1 <Linux内核原理与分析>教学进程 考核方案 第一周: 第二周: 第三周: 第四周: 第五周 第六周 第七周: 第八周 第九周 第十周 第十一周: 第十二周 ...

  8. 配置每次git push 不需要输入账号密码

    配置每次git push 不需要输入账号密码 .gitconfig文件地址 C:\Users\Admin

  9. RocketMQ多master迁移至多master多slave模式

    一.项目背景 由于当前生产环境RocketMQ机器使用年限较长,已经过保,并且其中一台曾经发生过异常宕机事件.并且早期网络规划较乱,生产.开发.测试等网络没有分开,公司决定对当前网络进行规划,区分各个 ...

  10. 机器学习(十)-------- 降维(Dimensionality Reduction)

    降维(Dimensionality Reduction) 降维的目的:1 数据压缩 这个是二维降一维 三维降二维就是落在一个平面上. 2 数据可视化 降维的算法只负责减少维数,新产生的特征的意义就必须 ...