介绍了如何做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需要这样引入)。
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)