背景
spring boot项目将jetty改成tomcat后,登录接口报错–登录接口有addCookie的逻辑
An invalid domain [xxxx] was specified for this cookie
全局搜索代码后,发现该提示来自tomcat-/tomcat-embed-core/9.0.46/tomcat-embed-core-9.0.46-sources.jar!/org/apache/tomcat/util/http/LocalStrings.properties。
进一步找到引用该提示语的地方为org.apache.tomcat.util.http.Rfc6265CookieProcessor
## 应用层addCookie的时候会走到这个方法
@Override
public String generateHeader(javax.servlet.http.Cookie cookie, HttpServletRequest request) {
StringBuffer header = new StringBuffer();
header.append(cookie.getName());
header.append('=');
String value = cookie.getValue();
if (value != null && value.length() > 0) {
validateCookieValue(value);
header.append(value);
}
... ...
String domain = cookie.getDomain();
if (domain != null && domain.length() > 0) {
# 校验cookie的domian属性
validateDomain(domain);
header.append("; Domain=");
header.append(domain);
}
... ...
return header.toString();
}
private static final BitSet domainValid = new BitSet(128);
static {
for (char c = '0'; c <= '9'; c++) {
domainValid.set(c);
}
for (char c = 'a'; c <= 'z'; c++) {
domainValid.set(c);
}
for (char c = 'A'; c <= 'Z'; c++) {
domainValid.set(c);
}
domainValid.set('.');
domainValid.set('-');
}
private void validateDomain(String domain) {
int i = 0;
int prev = -1;
int cur = -1;
char[] chars = domain.toCharArray();
while (i < chars.length) {
prev = cur;
cur = chars[i];
# cookie的domain属性值只能包含字母、数字、“-”、“.”
if (!domainValid.get(cur)) {
throw new IllegalArgumentException(sm.getString(
"rfc6265CookieProcessor.invalidDomain", domain));
}
# cookie的domain属性值第一个字母不能为“.”
if ((prev == '.' || prev == -1) && (cur == '.' || cur == '-')) {
throw new IllegalArgumentException(sm.getString(
"rfc6265CookieProcessor.invalidDomain", domain));
}
# cookie的domain属性值最后一个字母不能为“.”
if (prev == '-' && cur == '.') {
throw new IllegalArgumentException(sm.getString(
"rfc6265CookieProcessor.invalidDomain", domain));
}
i++;
}
... ...
}
解决方案
解决方案1-domian不以"."开头
以前cookie domain属性中开头的".",代表该cookie对子域名也是有效的。比如domain设成“.example.com”;
"www.example.com”、“www.corp.example.com”、"example.com"浏览器访问这三个网址的时候,都能将cookie自动带到服务端。
RFC 6265规范出来后,上述规则被修改了;浏览器会忽略domain属性中前缀“.”,而且自动对子域名生效。
上述内容可以参考https://stackoverflow.com/questions/9618217/what-does-the-dot-prefix-in-the-cookie-domain-mean。以及RFC 6265的官方说明。
The Domain attribute specifies those hosts to which the cookie will
be sent. For example, if the value of the Domain attribute is
“example.com”, the user agent will include the cookie in the Cookie
header when making HTTP requests to example.com, www.example.com, and
www.corp.example.com. (Note that a leading %x2E (“.”), if present,
is ignored even though that character is not permitted, but a
trailing %x2E (“.”), if present, will cause the user agent to ignore
the attribute.) If the server omits the Domain attribute, the user
agent will return the cookie only to the origin server.
解决方案2-替换tomcat默认的cookie处理器
legacy代表遗产、遗留的意思;LegacyCookieProcessor就表示对老的cookie的标准做的兼容。看源码可以发现LegacyCookieProcessor不会对domain的属性进行校验。
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
return (factory) -> factory.addContextCustomizers(
(context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
}