搭建大型分布式服务(二十)SpringBoot 如何防止SQL注入?
系列文章目录文章目录系列文章目录前言一、本文要点二、开发环境三、创建项目四、自定义校验五、测试一下六、小结前言群里有个小伙伴提问,SpringBoot项目怎样做参数校验,防止SQL注入?改动尽量少,高灵活,因为并不是每个参数有需要校验的。一、本文要点接前文,我们介绍了如何做spring拓展,轻松面对面试官的提问了。本文介绍如何自定义参数校验,保护我们的系统,避免各种参数问题。系列文章完整目录spr
·
系列文章目录
前言
群里有个小伙伴提问,SpringBoot项目怎样做参数校验,防止SQL注入?改动尽量少,高灵活,因为并不是每个参数有需要校验的。一、本文要点
接前文,我们介绍了如何做spring拓展,轻松面对面试官的提问了。本文介绍如何自定义参数校验,保护我们的系统,避免各种参数问题。系列文章完整目录
- springboot 自定义校验参数
- springboot 自定义校验规则
- springboot 注解
- springboot 校验枚举
- springboot 预防SQL注入
二、开发环境
- jdk 1.8
- maven 3.6.2
- springboot 2.4.3
- idea 2020
三、创建项目
1、使用早期文章快速创建项目。
《搭建大型分布式服务(十八)Maven自定义项目脚手架》
2、创建Book项目
mvn archetype:generate -DgroupId="com.mmc.lesson" -DartifactId="book" -Dversion=1.0-SNAPSHOT -Dpackage="com.mmc.lesson" -DarchetypeArtifactId=member-archetype -DarchetypeGroupId=com.mmc.lesson -DarchetypeVersion=1.0.0-SNAPSHOT -B
四、自定义校验
1、修改pom.xml,增加依赖(高版本的springboot需要这样引入)。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2、假设系统存在这样一段拼接SQL语句的方法,很明显,我们需要对uname字段做校验,防止别人传 " 1 or 1<>2 " 或者 " 1 or 1=1 – " 这样的值从而达到SQL注入。
if (StringUtils.hasText(req.getUname())) {
sql += " and uname = " + req.getUname();
}
3、定义注解@SqlInjection,作用在需要防止SQL注入的参数上。
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = SqlInjectionConstraintValidator.class)
@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface SqlInjection {
/**
* 错误提示.
*/
String message() default "Contains illegal characters";
/**
* groups.
*/
Class<?>[] groups() default {};
/**
* payload.
*/
Class<? extends Payload>[] payload() default {};
}
4、编写SqlInjectionConstraintValidator.java,使用正则表达式对特殊字符做校验。
@Slf4j
public class SqlInjectionConstraintValidator implements ConstraintValidator<SqlInjection, String> {
/**
* 预编译SQL过滤正则表达式
*/
private Pattern sqlPattern = Pattern.compile(
"(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|and|or|delete"
+ "|insert|trancate|char|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)");
@Override
public void initialize(SqlInjection constraintAnnotation) {
log.info("initialize constraint");
}
@Override
public boolean isValid(String source, ConstraintValidatorContext constraintValidatorContext) {
if (StringUtils.hasText(source)) {
Matcher matcher = sqlPattern.matcher(source);
if (matcher.find()) {
log.error("包含非法字符|{}", source);
return false;
}
}
return true;
}
}
5、增加@Validated注解,使我们自定义校验规则生效。
@GetMapping("/get")
public Result get(@Validated GetBookReq req) {
System.out.println("IndexController.get");
TblMemberInfo book = new TblMemberInfo();
book.setUid(8888L);
TblMemberInfo ret = bookService.get(book);
return Result.ok(ret);
}
五、测试一下
1、编写单元测试
@Slf4j
@ActiveProfiles("dev")
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class IndexControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testNormal() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/get")
.param("uname", "zhangsan") // 正常传惨
.characterEncoding("UTF-8")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code", IsEqual.equalTo(0)));
}
@Test
void testError() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/get")
.param("uname", "1 and 1<>1 -- ") // SQL 注入
.characterEncoding("UTF-8")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code", IsEqual.equalTo(-1)));
}
}
2、测试通过。
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'getBookReq' on field 'uname': rejected value [1 and 1<>1 -- ]; codes [SqlInjection.getBookReq.uname,SqlInjection.uname,SqlInjection.java.lang.String,SqlInjection]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [getBookReq.uname,uname]; arguments []; default message [uname]]; default message [Contains illegal characters]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:170)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
六、小结
至此,我们就简单实现了自定义参数校验功能啦。课后作业,小伙伴可以自行去实现枚举参数校验哦。下一篇《搭建大型分布式服务(二十一)Mybatis 如何打印SQL语句和执行时间?》
加我加群一起交流学习!更多干货下载和大厂内推等着你
更多推荐
所有评论(0)