你知道任何允许为cookie设置自定义标志的Java cookie实现,比如SameSite=strict
吗?似乎javax.servlet.http.Cookie
有一组严格限制的标志可以添加。
答案 0 :(得分:29)
我不是JEE专家,但我认为因为那个cookie属性是一个有点新的发明,所以你不能指望它出现在Java EE 7接口或实现中。似乎Cookie
类缺少通用属性的setter。但不是通过
HttpServletResponse
response.addCookie(myCookie)
您只需通过
设置相应的HTTP标头字段即可response.setHeader("Set-Cookie", "key=value; HttpOnly; SameSite=strict")
我希望这对你来说已经足够了。
P.S。:我已根据您的问题删除了我最近的一些评论,也许您还想删除现在不再需要的3条回复。随后我也可以在这里删除这一段。感谢。
答案 1 :(得分:7)
截止到今天(20.01.20),servlet-api
不允许为Cookie设置sameSite
属性。顺便说一句,正在进行中的票证(LINK)将发布新票证(5.0或5.1 servlet-api
)。
选项1:您并不着急,可以等待servlet-api
版本,其中Cookie
类和SessionCookieConfig
类具有专用的方法来设置{ {1}}属性。
选项2::您使用的是sameSite
的旧版本(例如3.1),因此是Tomcat的旧版本(例如,我现在有这种情况)。这意味着即使社区在servlet-api
支持下发布servlet-api
,您也无法立即更新版本,因为更新两个主要版本的风险太大。
在这种情况下,我们找到了解决方案。
Tomcat中有一个sameSite
LINK
CookieProcessor元素表示将接收到的cookie头解析为可通过HttpServletRequest.getCookies()访问的javax.servlet.http.Cookie对象,并转换通过HttpServletResponse.addCookie()添加到响应的javax.servlet.http.Cookie对象的组件。返回到客户端的HTTP标头。
此处理器的用法非常简单。在context.xml内部:
Cookie Processor Component
在这种情况下,使用默认的处理器实现(<Context>
...
<CookieProcessor sameSiteCookies="none"/>
</Context>
),但是您可以在org.apache.tomcat.util.http.Rfc6265CookieProcessor
属性CookieProcessor
中指定其他任何处理器。
答案 2 :(得分:5)
如果您已有代码,那么无疑已经使用了Java servlet Cookie对象。我们当然有,所以我们想要最少破坏性的选择。 @kriegaex的答案简洁明了,但基本上是对Cookie进行硬编码,并且不会重用cookie对象。为了扩展他的答案,我们编写了此函数来处理相同的站点功能,同时维护现有的Cookie对象功能。该答案旨在用于需要在响应对象上添加多个cookie的情况下,而无需更改标题中可能已经存在的现有cookie。当然,另一个选择是编写一个新的cookie类并扩展功能,但这需要对现有代码进行比我们在此提出的更多更改。
请注意,使用此解决方案,只需更改一行现有代码(每个cookie)即可添加相同的网站功能。
样品用量:
// Existing code that doesn't change:
Cookie cookie1=new Cookie("cookie1",Util.encodeURL(id));
cookie1.setHttpOnly(false);
cookie1.setPath("/");
Cookie cookie2=new Cookie("cookie2",Util.encodeURL(id));
cookie2.setHttpOnly(false);
cookie2.setPath("/");
// Old Code that is replaced by new code
// httpResponse.addCookie(cookie1);
// httpResponse.addCookie(cookie2);
// New Code - see static helper class below
HttpService.addCookie(httpResponse, cookie1, "none");
HttpService.addCookie(httpResponse, cookie2, "Strict");
使用cURL时的示例响应标头:
< HTTP/1.1 200 OK
< Connection: keep-alive
< X-Powered-By: Undertow/1
< Set-Cookie: cookie1=f871c026e8eb418c9c612f0c7fe05b08; path=/; SameSite=none; secure
< Set-Cookie: cookie2=51b405b9487f4487b50c80b32eabcc24; path=/; SameSite=Strict; secure
< Server: WildFly/9
< Transfer-Encoding: chunked
< Content-Type: image/png
< Date: Tue, 10 Mar 2020 01:55:37 GMT
最后是静态助手类:
public class HttpService {
private static final FastDateFormat expiresDateFormat= FastDateFormat.getInstance("EEE, dd MMM yyyy HH:mm:ss zzz", TimeZone.getTimeZone("GMT"));
public static void addCookie(HttpServletResponse response, Cookie cookie, String sameSite) {
StringBuilder c = new StringBuilder(64+cookie.getValue().length());
c.append(cookie.getName());
c.append('=');
c.append(cookie.getValue());
append2cookie(c,"domain", cookie.getDomain());
append2cookie(c,"path", cookie.getPath());
append2cookie(c,"SameSite", sameSite);
if (cookie.getSecure()) {
c.append("; secure");
}
if (cookie.isHttpOnly()) {
c.append("; HttpOnly");
}
if (cookie.getMaxAge()>=0) {
append2cookie(c,"Expires", getExpires(cookie.getMaxAge()));
}
response.addHeader("Set-Cookie", c.toString());
}
private static String getExpires(int maxAge) {
if (maxAge<0) {
return "";
}
Calendar expireDate = Calendar.getInstance();
expireDate.setTime(new Date());
expireDate.add(Calendar.SECOND,maxAge);
return expiresDateFormat.format(expireDate);
}
private static void append2cookie(StringBuilder cookie, String key, String value) {
if (key==null ||
value==null ||
key.trim().equals("")
|| value.trim().equals("")) {
return;
}
cookie.append("; ");
cookie.append(key);
cookie.append('=');
cookie.append(value);
}
}
答案 3 :(得分:2)
Jetty服务器版本9.4.26.v20200117
允许在cookie上设置SameSite
属性。我不得不做一些挖掘工作,但这是可行的。
import static org.eclipse.jetty.http.HttpCookie.SAME_SITE_STRICT_COMMENT;
...
Cookie cookie = new Cookie("my-cookie", "some-value");
cookie.setMaxAge(120); // age in seconds
cookie.setSecure(true);
cookie.setHttpOnly(true);
cookie.setComment(SAME_SITE_STRICT_COMMENT);
response.addCookie(cookie);
码头服务器的addCookie()
对象上的Response
方法对注释进行检查,以添加SameSite
属性。
答案 4 :(得分:2)
我尝试使用列出的解决方案使用javax.servlet.http.Cookie
设置SameSite=strict
属性,但是没有一个起作用。
但是,这种方法对我有效,使用javax.servlet.http.Cookie
(JRE 1.8 + JBOSS 7.X):
Cookie cookie = new Cookie(name, value);
path = path.concat("SameSite=Strict;");
cookie.setPath(path);
就是这样。经过测试
答案 5 :(得分:2)
如果将弹簧靴与Tom猫一起使用,则已在另一个问题中得到了回答。 总而言之,在tom cat配置上设置属性。这是全局的,所有cookie都将启用相同的站点。 (来自另一个问题https://stackoverflow.com/a/60860531/400048)
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Bean
public TomcatContextCustomizer sameSiteCookiesConfig() {
return context -> {
final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
context.setCookieProcessor(cookieProcessor);
};
}
答案 6 :(得分:2)
如果您碰巧使用 Spring Framework,则可以利用 ResponseCookie 类。例如:
final ResponseCookie responseCookie = ResponseCookie
.from("<my-cookie-name>", "<my-cookie-value-here>")
.secure(true)
.httpOnly(true)
.path("/auth")
.maxAge(12345)
.sameSite("Lax")
.build();
response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
免责声明:提供的标志及其值仅作为类 API 的示例。
答案 7 :(得分:1)
如果您不想更新所有代码,也可以使用Apache或Nginx配置(或您使用的任何其他HTTP服务器/代理)通过一行配置实现相同的目的
您可以将以下行添加到您的Apache配置中
Header always edit Set-Cookie (.*) "$1; SameSite=Lax"
这将使用SameSite=Lax
标志更新您的所有cookie
在此处查看更多信息:https://blog.giantgeek.com/?p=1872
location / {
# your usual config ...
# hack, set all cookies to secure, httponly and samesite (strict or lax)
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
}
与此处相同,这也会使用SameSite=Lax
标志更新您的所有cookie
在此处查看更多信息:https://serverfault.com/questions/849888/add-samesite-to-cookies-using-nginx-as-reverse-proxy
答案 8 :(得分:1)
我发现,成功返回时创建的cookie未被“标题编辑”或“标题始终编辑”更改。显然,apache有两个Cookie桶-参见this
对我有用的是
Header onsuccess edit Set-Cookie (.*) "$1; SameSite=Lax"
答案 9 :(得分:0)
不使用spring boot或spring session的解决方案。
有关解决方案的更多详细信息 Samesite for jessessionId cookie can be set only from response
package com.cookie.example.filters.cookie;
import com.google.common.net.HttpHeaders;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.annotation.Nonnull;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Implementation of an HTTP filter {@link Filter} which which allow customization of {@literal Set-Cookie} header.
* customization is delegated to implementations of {@link CookieHeaderCustomizer}
*/
public class CookieHeaderCustomizerFilter extends DelegatingFilterProxy implements InitializingBean {
private final List<CookieHeaderCustomizer> cookieHeaderCustomizers;
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
if(CollectionUtils.isEmpty(cookieHeaderCustomizers)){
throw new IllegalArgumentException("cookieHeaderCustomizers is mandatory");
}
}
public CookieHeaderCustomizerFilter(final List<CookieHeaderCustomizer> cookieHeaderCustomizers) {
this.cookieHeaderCustomizers = cookieHeaderCustomizers;
}
public CookieHeaderCustomizerFilter() {
this.cookieHeaderCustomizers = Collections.emptyList();
}
/** {@inheritDoc} */
public void destroy() {
}
/** {@inheritDoc} */
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Request is not an instance of HttpServletRequest");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("Response is not an instance of HttpServletResponse");
}
chain.doFilter(request, new CookieHeaderResponseWrapper((HttpServletRequest) request, (HttpServletResponse)response ));
}
/**
* An implementation of the {@link HttpServletResponse} which customize {@literal Set-Cookie}
*/
private class CookieHeaderResponseWrapper extends HttpServletResponseWrapper{
@Nonnull private final HttpServletRequest request;
@Nonnull private final HttpServletResponse response;
public CookieHeaderResponseWrapper(@Nonnull final HttpServletRequest req, @Nonnull final HttpServletResponse resp) {
super(resp);
this.request = req;
this.response = resp;
}
/** {@inheritDoc} */
@Override
public void sendError(final int sc) throws IOException {
applyCustomizers();
super.sendError(sc);
}
/** {@inheritDoc} */
@Override
public PrintWriter getWriter() throws IOException {
applyCustomizers();
return super.getWriter();
}
/** {@inheritDoc} */
@Override
public void sendError(final int sc, final String msg) throws IOException {
applyCustomizers();
super.sendError(sc, msg);
}
/** {@inheritDoc} */
@Override
public void sendRedirect(final String location) throws IOException {
applyCustomizers();
super.sendRedirect(location);
}
/** {@inheritDoc} */
@Override
public ServletOutputStream getOutputStream() throws IOException {
applyCustomizers();
return super.getOutputStream();
}
private void applyCustomizers(){
final Collection<String> cookiesHeaders = response.getHeaders(HttpHeaders.SET_COOKIE);
boolean firstHeader = true;
for (final String cookieHeader : cookiesHeaders) {
if (StringUtils.isBlank(cookieHeader)) {
continue;
}
String customizedCookieHeader = cookieHeader;
for(CookieHeaderCustomizer cookieHeaderCustomizer : cookieHeaderCustomizers){
customizedCookieHeader = cookieHeaderCustomizer.customize(request, response, customizedCookieHeader);
}
if (firstHeader) {
response.setHeader(HttpHeaders.SET_COOKIE,customizedCookieHeader);
firstHeader=false;
} else {
response.addHeader(HttpHeaders.SET_COOKIE, customizedCookieHeader);
}
}
}
}
}
/**
* Implement this interface and inject add it to {@link SameSiteCookieHeaderCustomizer}
*/
public interface CookieHeaderCustomizer {
String customize(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response, @Nonnull final String cookieHeader);
}
package com.cookie.example.filters.cookie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*Add SameSite attribute if not already exist
*SameSite attribute value is defined by property "cookie.sameSite"
*/
public class SameSiteCookieHeaderCustomizer implements CookieHeaderCustomizer {
private static final Logger LOGGER = LoggerFactory.getLogger(SameSiteCookieHeaderCustomizer.class);
private static final String SAME_SITE_ATTRIBUTE_NAME ="SameSite";
private static final String SECURE_ATTRIBUTE_NAME="Secure";
private final SameSiteValue sameSiteValue;
public SameSiteCookieHeaderCustomizer(SameSiteValue sameSiteValue) {
this.sameSiteValue = sameSiteValue;
}
@Override
public String customize(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response, @Nonnull final String cookieHeader) {
StringBuilder sb = new StringBuilder(cookieHeader);
if (!cookieHeader.contains(SAME_SITE_ATTRIBUTE_NAME)) {
sb.append("; ").append(SAME_SITE_ATTRIBUTE_NAME).append("=").append(sameSiteValue.value);
}
if(SameSiteValue.None == sameSiteValue && !cookieHeader.contains(SECURE_ATTRIBUTE_NAME)){
sb.append("; ").append(SECURE_ATTRIBUTE_NAME);
}
return sb.toString();
}
public enum SameSiteValue{
/**
* Send the cookie for 'same-site' requests only.
*/
Strict("Strict"),
/**
* Send the cookie for 'same-site' requests along with 'cross-site' top
* level navigations using safe HTTP methods (GET, HEAD, OPTIONS, and TRACE).
*/
Lax("Lax"),
/**
* Send the cookie for 'same-site' and 'cross-site' requests.
*/
None("None");
/** The same-site attribute value.*/
private String value;
/**
* Constructor.
*
* @param attrValue the same-site attribute value.
*/
SameSiteValue(@Nonnull final String attrValue) {
value = attrValue;
}
/**
* Get the same-site attribute value.
*
* @return Returns the value.
*/
public String getValue() {
return value;
}
}
}
答案 10 :(得分:0)
ravinder5 确实实现了这一点并将其开源:CookieHeader
示例用法:
import com.tgt.egs.auth.cookie.CookieHeader;
...
CookieHeader.createSetCookieHeader(cookieName, cookieValue, domain, path, sameSite, secure, httpOnly, expiry);