问题描述
自己搭建一个SpringBoot工程的时候,集成了Swagger版本信息
·SpringBoot:2.1.6.RELEASE
spring-boot-admin:2.1.5
swagger-spring-boot-starter:1.4.5-RELEASE
spring-boot-starter-security:2.1.6.RELEASE
对应的
spring-security:5.1.5.RELEASE
工程配置文件
# 相对于yml配置,properties优先级高
server:
port: 8080
servlet:
context-path: /
# 这个配置尽量不要写到application配置表中
person:
age: 25
name: kevin
# 配置日志等级
logging:
# pattern:
# console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){green}"
path: ./logs/
level:
root: info
org:
springframework:
web: debug
file:
max-history: 10
max-size: 10MB
spring:
profiles:
active: dev
# 配置控制台打印颜色
output:
ansi:
enabled: always
#配置管理平台
boot:
admin:
client:
url: "http://localhost:8080"
instance:
prefer-ip: true
# swgger扫描包路径
swagger:
base-package: "com.itkevin"
enabled: true
# 加载所有的端点/默认只加载了 info / health
management:
endpoints:
web:
exposure:
include: "*"
base-path: "/"
endpoint:
health:
show-details: always
# 描述信息
info:
blog-url: "is kevin"
author: "kevin"
version: "@project.version@"
配置完成之后进入swagger页面访问
http://localhost:8080/swagger-ui.html
结果:
打开调试工具
原来是这个接口报错了,发现这个接口url
http://localhost:8080//v2/api-docs
竟然有个
//
,但是我记得springmvc是可以解析这种url的啊?
打开后台看看报错
2019-07-07 12:48:48.799 ERROR 53628 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL was not normalized.
at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:296) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:194) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:114) ~[spring-boot-actuator-2.1.6.RELEASE.jar:2.1.6.RELEASE]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:104) ~[spring-boot-actuator-2.1.6.RELEASE.jar:2.1.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.21.jar:9.0.21]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.21.jar:9.0.21]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]
错误报错:The request was rejected because the URL was not normalized.
翻译过来:请求被拒绝,因为URL未规范化
。
看看源码到底是哪里报错:
public class StrictHttpFirewall implements HttpFirewall {
...
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
rejectForbiddenHttpMethod(request);
rejectedBlacklistedUrls(request);
if (!isNormalized(request)) {
throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
}
String requestUri = request.getRequestURI();
if (!containsOnlyPrintableAsciiCharacters(requestUri)) {
throw new RequestRejectedException("The requestURI was rejected because it can only contain printable ASCII characters.");
}
return new FirewalledRequest(request) {
@Override
public void reset() {
}
};
}
private static boolean isNormalized(String path) {
if (path == null) {
return true;
}
//老版本的并没有这个校验
if (path.indexOf("//") > -1) {
return false;
}
for (int j = path.length(); j > 0;) {
int i = path.lastIndexOf('/', j - 1);
int gap = j - i;
if (gap == 2 && path.charAt(i + 1) == '.') {
// ".", "/./" or "/."
return false;
} else if (gap == 3 && path.charAt(i + 1) == '.' && path.charAt(i + 2) == '.') {
return false;
}
j = i;
}
return true;
}
...
}
看源码就找到错误原因了,新版本的SpringSecurity在校验url规范的时候添加了//
的校验,所以出现这个符号之后,后台会直接报错,而swagger这个在组装url的时候显然出现了这样的bug,这么看来,这个坑其实算是swagger的,可能因为swagger开发人员的疏忽导致这个问题。
整改方法:
- 强制自定义防火墙
@Bean
public HttpFirewall defaultHttpFirewall() {
return new DefaultHttpFirewall();
}
@Override
public void configure(WebSecurity web) throws Exception {
//新防火墙强制覆盖原来的
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
// super.configure(web);
}
- 修改swagger源码,修复
//
的bug - 网上查到的方法(我自己测试无法使用这种方法)
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
//此处开启不让检测‘/’符号
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
@Override
public void configure(WebSecurity web) throws Exception {
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
// super.configure(web);
}
其实在这里说一下,低版本的SpringSecurity并没有做这个校验