问题描述
SpringCloudGateway中使用Spring Cloud Security的basic auth验证,会导致请求响应增加100毫秒左右的耗时。发布该博文时,SpringCloudGateway已经升级到了2.2.2,但是问题依旧,也许在后续的版本会解决。
使用SpringCloudSecurity的配置如下
1 | import org.springframework.context.annotation.Bean; |
application.yaml配置1
2
3
4
5
6
7spring:
security:
user:
password: app_password
name: mall_app
basic:
enabled: true
spring cloud gateway 是2.1.2版本
spring-boot-starter-security 是2.1.5
通过以上方式请求,响应时间会莫名其妙多100毫秒左右,简直无法接受。
跟踪日志如下
1 | 2019-10-23 18:54:57.640 DEBUG 19404 --- [ctor-http-nio-3] r.n.http.server.HttpServerOperations : [id: 0x138ab754, L:/0:0:0:0:0:0:0:1:8110 - R:/0:0:0:0:0:0:0:1:62011] Increasing pending responses, now 1 |
仔细看以上的时间变化会发现有一个地方突然增加了100毫秒,通过研读源代码,依然没能解决,估计是框架的bug,故曲线救国,即要能实现basic验证,又要确保不会莫名延迟100毫秒,故通过全局过滤器解决。
详细解决方式如下:
1 去掉spring-boot-starter-security的maven依赖
2 增加以下全局过滤器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Base64;
import java.util.List;
/**
* basic验证过滤器
*
* @author mafgwo
* @since 2019/10/23
*/
@Component
public class BasicAuthFilter implements GlobalFilter, Ordered {
private static final String BASIC = "basic";
private static final String[] EXCLUDE_URI_PREFIXS = {"/health", "/smartcall/"};
@Value("${spring.security.user.name}")
private String username;
@Value("${spring.security.user.password}")
private String password;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 直接跳过
for (int i = 0; i < EXCLUDE_URI_PREFIXS.length; i++) {
if (request.getURI().getPath().startsWith(EXCLUDE_URI_PREFIXS[i])) {
return chain.filter(exchange);
}
}
ServerHttpResponse response = exchange.getResponse();
// 获取header 无请求头则直接返回失败
List<String> authList = request.getHeaders().get(HttpHeaders.AUTHORIZATION);
if (CollectionUtils.isEmpty(authList)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return Mono.empty();
}
// 取出第一个值
String basicToken = authList.get(0);
String[] tokenArr = basicToken.split(" ");
if (tokenArr.length != 2 || !BASIC.equalsIgnoreCase(tokenArr[0].trim())) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return Mono.empty();
}
String token = tokenArr[1].trim();
// 取出
String userMsg = username + ":" + password;
String authorization = Base64.getEncoder().encodeToString(userMsg.getBytes());
if (!token.equalsIgnoreCase(authorization)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return Mono.empty();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
}