遇到个 Java 和 Php 在获取客户端 cookie 方式不同导致跨系统的问题。所以写了这篇博客梳理下相关知识。
实验
下面通过两个简单的实验,来看Java和Php在获取web请求中的cookie的不同之处,我下面贴出http请求的相关信息,和服务端输出的结果。
Java
请求信息
GET / HTTP/1.1
Host: localhost:7003
...
Cookie: test2=ab+cd; test1=ab%2Bcd
服务端
@Controller
@Slf4j
public class MainController {
@Autowired
private HttpServletRequest request;
@GetMapping("/")
public @ResponseBody
String index() {
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie cookie : cookies) {
log.info(cookie.getName() + "=" + cookie.getValue());
}
}
return "index";
}
}
控制台输出
2019-05-16 18:03:32.770 INFO 10114 --- [nio-7003-exec-1] net.mengkang.demo.MainController : test2=ab+cd
2019-05-16 18:03:32.770 INFO 10114 --- [nio-7003-exec-1] net.mengkang.demo.MainController : test1=ab%2Bcd
Php
GET / HTTP/1.1
Host: localhost:8084
...
Cookie: test2=ab+cd; test1=ab%2Bcd
服务端
var_exprot($_COOKIE);
array (
'test2' => 'ab cd',
'test1' => 'ab+cd',
)
结果对比
发现Java是不会对cookie数据做任何处理,但是php则会默认进行一次urldecode操作,这导致了,两边系统里面获取同一cookie时,结果不一致的 bug。
Php 源码分析
主要查看两处源码
main/php_variables.c
ext/standard/url.c
SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data)
{
...
switch (arg) {
case PARSE_GET:
case PARSE_STRING:
separator = PG(arg_separator).input;
break;
case PARSE_COOKIE:
separator = "; "; //可以在我们浏览器里看到请求的header里面cookie的分隔符就是这个
break;
}
var = php_strtok_r(res, separator, &strtok_buf);
while (var) {
val = strchr(var, '=');
if (arg == PARSE_COOKIE) {
/* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a space */
while (isspace(*var)) {
var++;
}
if (var == val || *var == '