目录
创建自定义异常类
封装Web返回对象
利用Swagger搭建REST API
配置Swagger
一、添加依赖库
二、创建Swagger配置类
三、编写测试Web接口
四、修改全局的配置文件
五、测试Web接口
配置后端验证功能
一、添加依赖
二、创建Form类
三、修改sayHello()方法
四、执行测试
抵御即跨站脚本(XSS)攻击
一、XSS攻击的危害
二、导入依赖库
三、定义请求包装类
四、创建过滤器,把所有请求对象传入包装类
五、给主类添加注解
六、测试拦截XSS脚本
因为后台Java项目是Web工程,所以有异常消息,我们要在原有异常消息的基础之上,封装状态码,所以需要我们自己创建一个异常类。
自定义异常类继承的父类,我没有选择Exception。因为Exception类型的异常,我们必须要手动显式处理,要么上抛,要么捕获。我希望我定义的异常采用既可以采用显式处理,也可以隐式处理,所以我选择继承RuntimeException这个父类。RuntimeException类型的异常可以被虚拟隐式处理,这样就省去了我们很多手动处理异常的麻烦。
1. 创建 com.example.emos.wx.exception 包
2. 创建EmosException类
package com.example.emos.wx.exception;
import lombok.Data;
@Data
public class EmosException extends RuntimeException{
private String msg;
private int code = 500;
public EmosException(String msg) {
super(msg);
this.msg = msg;
}
public EmosException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public EmosException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public EmosException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
}
虽然SpringMVC的Controller可以自动把对象转换成JSON返回给客户端,但是我们需要制定一个统一的标准,保证所有Controller返回的数据格式一致。最简便的办法就是定义封装类,来统一封装返回给客户端的数据。
1. 修改 pom.xml 文件,添加依赖库。 Apache 的 httpcomponents 库里面的 HttpStatus 类封装了很多状态码,所以我们在Web返回对象中封装状态吗,可以用到这些状态码。添加依赖后Maven重新加载项目。
org.apache.httpcomponents
httpcore
4.4.13
2. 创建 com.example.emos.wx.common.util 包,然后创建 R 类
3. 代码
package com.example.emos.wx.common.util;
import org.apache.http.HttpStatus;
import java.util.HashMap;
import java.util.Map;
public class R extends HashMap {
public R() {
put("code", HttpStatus.SC_OK);
put("msg", "success");
}
public static R error() {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
开发前后端分离架构的项目,往往调试后端Web接口需要用到POSTMAN工具。虽然POSTMAN工具的功能非常强大,但是请求参数很多的情况下,我们手写这些参数和数据还是非常麻烦的。因此我们需要一个调试后端Web接口更加简便的方法。恰好Swagger提供了REST API调用方式,我们不需要借助任何工具的情况下,访问Swagger页面,就可以对Web接口进行调用和调试,这种调试方式的效率要远超POSTMAN软件。
1. (ApiInfoBuilder)定义Swagger页面基本信息
2. (ApiSelectorBuilder)哪些类中的方法会出现在Swagger上面
3. 开启对JWT的支持
·List
·AuthorizationScope[]: JWT认证在Swagger中的作用域
·List
·List
·Docket
把配置信息给Spring
在 pom.xml 文件中添加Swagger依赖库,这里我们使用的是Swagger2版本,在UI方面,比 Swagger1版本要好看很多。
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
在 com.example.emos.wx.config 包中创建 SwaggerConfig 类
package com.example.emos.wx.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
Docket docket = new Docket(DocumentationType.SWAGGER_2);
// ApiInfoBuilder 用于在Swagger界面上添加各种信息
ApiInfoBuilder builder = new ApiInfoBuilder();
builder.title("EMOS在线办公系统");
ApiInfo apiInfo = builder.build();
docket.apiInfo(apiInfo);
// ApiSelectorBuilder 用来设置哪些类中的方法会生成到REST API中
ApiSelectorBuilder selectorBuilder = docket.select();
selectorBuilder.paths(PathSelectors.any()); //所有包下的类
//使用@ApiOperation的方法会被提取到REST API中
selectorBuilder.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class));
docket = selectorBuilder.build();
/*
* 下面的语句是开启对JWT的支持,当用户用Swagger调用受JWT认证保护的方法,
* 必须要先提交参数(例如令牌)
*/
//存储用户必须提交的参数
List apikey = new ArrayList();
//规定用户需要输入什么参数
apikey.add(new ApiKey("token", "token", "header"));
docket.securitySchemes(apikey);
//如果用户JWT认证通过,则在Swagger中全局有效
AuthorizationScope scope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] scopeArray = {scope};
//存储令牌和作用域
SecurityReference reference = new SecurityReference("token", scopeArray);
List refList = new ArrayList();
refList.add(reference);
SecurityContext context =
SecurityContext.builder().securityReferences(refList).build();
List cxtList = new ArrayList();
cxtList.add(context);
docket.securityContexts(cxtList);
return docket;
}
}
在 com.example.emos.wx.controller 包中创建 TestController 类。
package com.example.emos.wx.controller;
import com.example.emos.wx.common.util.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
@Api("测试Web接口")
public class TestController {
@GetMapping("/sayHello")
@ApiOperation("最简单的测试方法")
public R sayHello(){
return R.ok().put("message","HelloWorld");
}
}
不修改会导致错误,就下面出现的问题。
because the return value of “springfox.documentation.spi.service.contexts.Orderings.patternsCondition(springfox.documentation.RequestHandler)” is null
在application.yml文件中增加配置,配置内容如下:
spring:
mvc:
pathmatch:
matching-strategy: ant-path-matcher
如Spring Boot 2.6发行说明中所述,您可以通过在application.properties文件中将Spring.mvc.pathmatch.matching-strategy设置为ant path matcher来恢复Springfox假定将使用的配置。请注意,只有在不使用Spring Boot的执行器时,此功能才起作用。无论配置的匹配策略如何,执行器始终使用基于路径模式的解析。如果您想在Spring Boot 2.6及更高版本中将其与执行器一起使用,则需要对Springfox进行更改。
因为在springboot2.6之后,将springmvc的默认匹配策略修改为了PathPatternParser,需要将其修改为AntPathMatcher就可以解决问题
打开浏览器,访问 http://127.0.0.1:8080/emos-wx-api/swagger-ui.html
库:Validation
创建Form类
·类声明要添加 @ApiModel【因为要出现在Swagger页面里】
·属性声明要添加 @ApiModelProperty【因为要出现在Swagger页面里】
·属性声明要添加验证注解 @NotNull @NotBlank @Min @Max @Range @Pattern
验证数据要使用 @Valid 注解
当用户提交请求的时候,SpringBoot项目就会把提交的数据封装到Form对象里面。同时执行后端的验证,如果数据满足要求,那么Web方法就可以正常的执行。如果不满足要求就会抛出异常,返回错误信息给客户端。
对于客户端提交表单或者Ajax中的数据,后端的Controller必须先要做验证,然后才能使用这些数据。既然要验证数据,那么不妨我们来使用一下 Validation 库。
在 pom.xml 文件中添加依赖,然后让Maven加载依赖库。
org.springframework.boot
spring-boot-starter-validation
validation 库在做后端验证的时候,要求必须用封装类(Form类)来保存客户端提交的数据, 然后在封装类中,我们可以定义验证的规则, validation 会执行这些规则,帮我们验证客户端 提交的数据。
我们为之前的 TestController 里面的 sayHello() 方法设置一个Form类,接受客户端提交的 name 数据。我们在 com.example.emos.wx.controller.form 包里面创建 TestSayHelloForm 类。
package com.example.emos.wx.controller.form;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@ApiModel
@Data
public class TestSayHelloForm {
@NotBlank
@Pattern(regexp = "^[\\u4e00-\\u9fa5]{2,15}$")
@ApiModelProperty("姓名")
private String name;
}
package com.example.emos.wx.controller;
import com.example.emos.wx.common.util.R;
import com.example.emos.wx.controller.form.TestSayHelloForm;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.Validation;
@RestController
@RequestMapping("/test")
@Api("测试Web接口")
public class TestController {
@PostMapping("/sayHello")
@ApiOperation("最简单的测试方法")
public R sayHello(@Valid @RequestBody TestSayHelloForm form){
return R.ok().put("message","Hello,"+form.getName());
}
}
打开浏览器,访问 http://127.0.0.1:8080/emos-wx-api/swagger-ui.html
库:Hutool
对Http请求中的数据转义
·设置过滤器
·覆盖Http请求的方法
HttpServletRequest是接口,各家服务器厂商会实现它。
如果直接继承各厂商的请求父类,那么我们的程序就跟厂商绑定在一起。
HttpServletRequestWrapper类使用了装饰器模式,装饰器封装了厂商的HttpServletRequest,只需要覆盖Wrapper类的方法,就能做到覆盖厂商请求对象里方法。
创建过滤器,把Request对象传入Wrapper对象。
getInputStream方法
非常重要。SpringMVC框架就是通过这个方法,从请求里面提取客户端提交的数据,然后把这些数据封装到Form对象里面。如果我们不对 getInputStream 方法读取的数据做转义,那么后端项目就不具备抵御跨站脚本攻击的能力。
Java不支持原生JSON格式
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种 内容。
例如用户在发帖或者注册的时候,在文本框中输入 ,这段代码 如果不经过转义处理,而直接保存到数据库。将来视图层渲染HTML的时候,把这段代码输出到页面上,那么 ,然后观察返回的结果。