SameSite cookie in Java application

后端 未结 9 1967
没有蜡笔的小新
没有蜡笔的小新 2020-12-24 01:08

Do you know any Java cookie implementation which allows to set a custom flag for cookie, like SameSite=strict? It seems that javax.servlet.http.Cookie has a str

相关标签:
9条回答
  • 2020-12-24 01:27

    Solution without using spring boot or spring session.

    for more details about the solution 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;
          }
    
        }
    
      }
    
    0 讨论(0)
  • 2020-12-24 01:31

    I found that our cookies which were being created on a successful return were not changed by "Header edit" or "Header always edit". Apparently apache has two buckets of cookies - see this

    What did work for me was

    Header onsuccess edit Set-Cookie (.*) "$1; SameSite=Lax"
    
    0 讨论(0)
  • 2020-12-24 01:34

    I am not a JEE expert, but I think that because that cookie property is a somewhat new invention, you cannot expect it to be present in Java EE 7 interfaces or implementations. The Cookie class is missing a setter for generic properties, as it seems. But instead of adding the cookie to your HttpServletResponse via

    response.addCookie(myCookie)
    

    you can simply set the corresponding HTTP header field via

    response.setHeader("Set-Cookie", "key=value; HttpOnly; SameSite=strict")
    

    Update: Thanks to @mwyrzyk for pointing out that setHeader() overwrites all existing headers of the same name. So if you happen have other Set-Cookie headers in your response already, of course you would use addHeader() with the same parameters instead.

    0 讨论(0)
提交回复
热议问题