Cookies 只能从响应设置jessessionId cookie的Samesite

Cookies 只能从响应设置jessessionId cookie的Samesite,cookies,browser,cross-browser,jsessionid,samesite,Cookies,Browser,Cross Browser,Jsessionid,Samesite,我试着让同一件事不再发生;来自java筛选器的my jsessionid cookie的安全性。我已经在响应集cookie头中添加了这个。在这个更改之后,请求cookie jsessionId是相同的。在响应中,jsessionId使用Samesite属性None和secure进行修改。如果请求jsessionId cookie保持不变,它是否会工作 对ServletResponse方法的调用:sendError、getWrite.flush()、sendRedirect、getOutputSt

我试着让同一件事不再发生;来自java筛选器的my jsessionid cookie的安全性。我已经在响应集cookie头中添加了这个。在这个更改之后,请求cookie jsessionId是相同的。在响应中,jsessionId使用Samesite属性None和secure进行修改。如果请求jsessionId cookie保持不变,它是否会工作

对ServletResponse方法的调用:sendError、getWrite.flush()、sendRedirect、getOutputStream.flush提交响应,这意味着状态代码和标头将在更新Set-cookie标头之前写入。

解决方案1:您可以将筛选器放在任何可能导致调用上述方法的筛选器之前,并在调用filterChain.doFilter之前修改标头

解决方案2:拦截对此方法的调用,并在提交响应之前更新标头

    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;
      }
  
    }
  
  }
package com.cookie.example.filters.cookie;
导入com.google.common.net.HttpHeaders;
导入org.apache.commons.collections.CollectionUtils;
导入org.apache.commons.lang3.StringUtils;
导入org.springframework.beans.factory.initializebean;
导入org.springframework.web.filter.DelegatingFilterProxy;
导入javax.annotation.Nonnull;
导入javax.servlet.*;
导入javax.servlet.http.HttpServletRequest;
导入javax.servlet.http.HttpServletResponse;
导入javax.servlet.http.HttpServletResponseWrapper;
导入java.io.IOException;
导入java.io.PrintWriter;
导入java.util.Collection;
导入java.util.Collections;
导入java.util.List;
/**
*HTTP筛选器{@link filter}的实现,该筛选器允许自定义{@literal Set Cookie}头。
*自定义委托给{@link CookieHeaderCustomizer}的实现
*/
公共类CookieHeaderCustomizerFilter扩展DelegatingFilterProxy实现初始化bean{
私人最终名单cookieHeaderCustomizers;
@凌驾
在PropertieSet()引发ServletException后公共无效{
super.afterPropertiesSet();
if(CollectionUtils.isEmpty(cookieHeaderCustomizers)){
抛出新的IllegalArgumentException(“cookieHeaderCustomizers是强制性的”);
}
}
公共CookieHeaderCustomizerFilter(最终列表CookieHeaderCustomizer){
this.cookieHeaderCustomizers=cookieHeaderCustomizers;
}
公共CookieHeaderCustomizerFilter(){
this.cookieHeaderCustomizers=Collections.emptyList();
}
/**{@inheritardoc}*/
公共空间销毁(){
}
/**{@inheritardoc}*/
公共筛选器(最终ServletRequest请求、最终ServletResponse响应、最终筛选器链)
抛出IOException、ServletException{
if(!(HttpServletRequest的请求实例)){
抛出新的ServletException(“请求不是HttpServletRequest的实例”);
}
if(!(HttpServletResponse的响应实例)){
抛出新的ServletException(“响应不是HttpServletResponse的实例”);
}
doFilter(请求,新CookieHeaderResponseWrapper((HttpServletRequest)请求,(HttpServletResponse)响应));
}
/**
*{@link HttpServletResponse}的一个实现,它定制了{@literal Set Cookie}
*/
私有类CookieHeaderResponseWrapper扩展了HttpServletResponseWrapper{
@非空私有最终HttpServletRequest请求;
@非空私有最终HttpServletResponse响应;
公共CookieHeaderResponseWrapper(@Nonnull final-HttpServletRequest-req,@Nonnull final-HttpServletResponse-resp){
超级(resp);
这个.request=req;
此响应=响应;
}
/**{@inheritardoc}*/
@凌驾
public void senderError(final int sc)引发IOException{
applyCustomizers();
超级发送错误(sc);
}
/**{@inheritardoc}*/
@凌驾
公共PrintWriter getWriter()引发IOException{
applyCustomizers();
返回super.getWriter();
}
/**{@inheritardoc}*/
@凌驾
public void senderor(final int sc,final String msg)引发IOException{
applyCustomizers();
超级发送错误(sc,msg);
}
/**{@inheritardoc}*/
@凌驾
public void sendRedirect(最终字符串位置)引发IOException{
applyCustomizers();
super.sendRedirect(位置);
}
/**{@inheritardoc}*/
@凌驾
公共ServletOutputStream getOutputStream()引发IOException{
applyCustomizers();
返回super.getOutputStream();
}
私有void applyCustomizers(){
最终集合cookiesHeaders=response.getHeaders(HttpHeaders.SET_COOKIE);
布尔值firstHeader=true;
for(最终字符串CookieSheader:cookiesHeaders){
if(StringUtils.isBlank(cookieHeader)){
继续;
}
字符串customizedCookieHeader=cookieHeader;
用于(CookieHeaderCustomizer CookieHeaderCustomizer:cookieHeaderCustomizers){
customizedCookieHeader=cookieHeaderCustomizer.customize(请求、响应、customizedCookieHeader);
}
如果(第一个标题){
setHeader(HttpHeaders.SET_COOKIE,customizedCookieHeader);
firstHeader=false;
}否则{
addHeader(HttpHeaders.SET_COOKIE,customizedCookieHeader);
}
}
}
}
}
/**
*实现此接口并将其添加到{@link SameSiteCookieHeaderCustomizer}
*/