【问题标题】:How to add multiple "Set-Cookie" header in servlet response?如何在 servlet 响应中添加多个“Set-Cookie”标头?
【发布时间】:2015-09-11 19:34:03
【问题描述】:

根据 RFC https://www.rfc-editor.org/rfc/rfc6265#page-7 允许有两个具有相同“Set-Cookie”键的标头。 RFC 中提供的示例是 -

​​Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
Set-Cookie: ​​lang=en-US; Path=/; Domain=example.com

如何使用 Jetty(或任何其他 servlet 容器)实现相同的目标?当我以这种方式调用 httpServletResponse.addHeader 时-

​httpServletResponse.addHeader("Set-Cookie", "SID=31d4d96e407aad42; Path=/; Secure; HttpOnly");
httpServletResponse.addHeader("Set-Cookie", "lang=en-US; Path=/; Domain=example.com");​

我看到第二个 addHeader() 没有添加新标题。根据这个方法的javadoc-

添加具有给定名称和值的响应标头。这种方法 允许响应头有多个值。

所以似乎允许多个值,但我不确定如何在 servlet 响应中设置多个“Set-Cookie”。

【问题讨论】:

  • 这在 Jetty 9 上按原样工作,完全符合您的预期,结果与@defectus(下)显示的结果相同

标签: java tomcat servlets http-headers jetty


【解决方案1】:

这可能不是您正在寻找的答案,但我自己尝试过,它立即奏效:

Set-Cookie:SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
Set-Cookie:lang=en-US; Path=/; Domain=example.com
Set-Cookie:JSESSIONID=76A68D96ED044DDFF0CC266810F52DDA; Path=/; HttpOnly

这就是响应的样子。可能是您的特定 Web 容器或您的实现的问题。

尝试调试应用程序(使用远程调试工具)以找出标头丢失的位置。

【讨论】:

  • 您使用的是哪个 servlet 容器? (码头/tomcat等)也,你能分享你的代码吗?非常感谢您试用!
【解决方案2】:

考虑到 Servlet API 有专门用于处理 Cookie 的方法,像这样直接设置 Cookie 有点尴尬。

无论如何,在 Jetty 9.3.0.v20150612 上进行了测试,它可以按预期工作。

示例:SetCookieTest.java

package jetty;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class SetCookieTest
{
    @SuppressWarnings("serial")
    public static class SetCookieAddHeaderServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            resp.setContentType("text/plain");
            resp.addHeader("Set-Cookie","SID=31d4d96e407aad42; Path=/; Secure; HttpOnly");
            resp.addHeader("Set-Cookie","lang=en-US; Path=/; Domain=example.com");
            PrintWriter out = resp.getWriter();
            out.println("Hello From: " + this.getClass().getName());
        }
    }

    @SuppressWarnings("serial")
    public static class SetCookieAddCookieServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            resp.setContentType("text/plain");

            // Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
            Cookie sidCookie = new Cookie("SID","31d4d96e407aad42");
            sidCookie.setPath("/");
            sidCookie.setSecure(true);
            sidCookie.setHttpOnly(true);
            resp.addCookie(sidCookie);

            // Set-Cookie: lang=en-US; Path=/; Domain=example.com
            Cookie langCookie = new Cookie("lang","en-US");
            langCookie.setPath("/");
            langCookie.setDomain("example.com");
            resp.addCookie(langCookie);

            PrintWriter out = resp.getWriter();
            out.println("Hello From: " + this.getClass().getName());
        }
    }

    private static Server server;

    @BeforeClass
    public static void startServer() throws Exception
    {
        server = new Server(9090);
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.addServlet(SetCookieAddHeaderServlet.class,"/test-add-header");
        context.addServlet(SetCookieAddCookieServlet.class,"/test-add-cookie");
        server.setHandler(context);
        server.start();
    }

    @AfterClass
    public static void stopServer() throws Exception
    {
        server.stop();
    }

    /**
     * Issue simple GET request, returning entire response (including payload)
     * 
     * @param uri
     *            the URI to request
     * @return the response
     */
    private String issueSimpleHttpGetRequest(String path) throws IOException
    {
        StringBuilder req = new StringBuilder();
        req.append("GET ").append(path).append(" HTTP/1.1\r\n");
        req.append("Host: localhost\r\n");
        req.append("Connection: close\r\n");
        req.append("\r\n");

        // Connect
        try (Socket socket = new Socket("localhost",9090))
        {
            try (OutputStream out = socket.getOutputStream())
            {

                // Issue Request
                byte rawReq[] = req.toString().getBytes(StandardCharsets.UTF_8);
                out.write(rawReq);
                out.flush();

                // Read Response
                StringBuilder resp = new StringBuilder();
                try (InputStream stream = socket.getInputStream();
                        InputStreamReader reader = new InputStreamReader(stream);
                        BufferedReader buf = new BufferedReader(reader))
                {
                    String line;
                    while ((line = buf.readLine()) != null)
                    {
                        resp.append(line).append(System.lineSeparator());
                    }
                }

                // Return Response
                return resp.toString();
            }
        }
    }

    @Test
    public void testAddHeader() throws Exception
    {
        String response = issueSimpleHttpGetRequest("/test-add-header");
        System.out.println(response);
        assertThat("response", response, containsString("Set-Cookie: SID=31d"));
        assertThat("response", response, containsString("Set-Cookie: lang=en-US"));
    }

    @Test
    public void testAddCookie() throws Exception
    {
        String response = issueSimpleHttpGetRequest("/test-add-cookie");
        System.out.println(response);
        assertThat("response", response, containsString("Set-Cookie: SID=31d"));
        assertThat("response", response, containsString("Set-Cookie: lang=en-US"));
    }
}

控制台输出

2015-06-25 14:18:19.186:INFO::main: Logging initialized @167ms
2015-06-25 14:18:19.241:INFO:oejs.Server:main: jetty-9.3.0.v20150612
2015-06-25 14:18:19.276:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@56cbfb61{/,null,AVAILABLE}
2015-06-25 14:18:19.288:INFO:oejs.ServerConnector:main: Started ServerConnector@1ef05443{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
2015-06-25 14:18:19.289:INFO:oejs.Server:main: Started @270ms
HTTP/1.1 200 OK
Date: Thu, 25 Jun 2015 21:18:19 GMT
Content-Type: text/plain;charset=iso-8859-1
Set-Cookie: SID=31d4d96e407aad42;Path=/;Secure;HttpOnly
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: lang=en-US;Path=/;Domain=example.com
Connection: close
Server: Jetty(9.3.0.v20150612)

Hello From: jetty.SetCookieTest$SetCookieAddCookieServlet

HTTP/1.1 200 OK
Date: Thu, 25 Jun 2015 21:18:19 GMT
Content-Type: text/plain;charset=iso-8859-1
Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
Set-Cookie: lang=en-US; Path=/; Domain=example.com
Connection: close
Server: Jetty(9.3.0.v20150612)

Hello From: jetty.SetCookieTest$SetCookieAddHeaderServlet

2015-06-25 14:18:19.405:INFO:oejs.ServerConnector:main: Stopped ServerConnector@1ef05443{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
2015-06-25 14:18:19.407:INFO:oejsh.ContextHandler:main: Stopped o.e.j.s.ServletContextHandler@56cbfb61{/,null,UNAVAILABLE}

【讨论】:

  • 您的测试代码的输出仅显示一个“Set-Cookie”标头;它将两个“Set-Cookie”标题合并为一个。我不想要这种行为。我想看两个 Set-Cookie 标头。如果您登录 wordpress 甚至 Google,Web 服务器会发送两个“Set-Cookie”标头。我正在开发一个代理,它需要按原样通过这些标头而不合并。
  • RFC2616 有一个更新,表明基于 Set-Cookie 的标头有不同的行为 - 请参阅 RFC7230 / Section 3.2.2 - 在 bugs.eclipse.org/471046 的 Eclipse Jetty 错误跟踪器上打开了一个关于它的问题
  • 使用“原始”HTTP 响应(不通过 HttpURLConnection 解释)更新了答案 - 这显示了多个 Set-Cookie 标头,就像您所追求的那样。
猜你喜欢
  • 2012-07-17
  • 2011-01-28
  • 1970-01-01
  • 2021-04-03
  • 2012-09-30
  • 1970-01-01
  • 2016-03-14
  • 2021-02-03
  • 2016-05-22
相关资源
最近更新 更多