如何在 servlet 响应中添加多个 "Set-Cookie" header?
How to add multiple "Set-Cookie" header in servlet response?
根据 RFC https://www.rfc-editor.org/rfc/rfc6265#page-7 允许有两个 header 具有相同的键“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() 没有添加新的 header。根据此方法的 javadoc-
Adds a response header with the given name and value. This method
allows response headers to have multiple values.
所以似乎允许多个值,但我不确定如何在 servlet 响应中使用多个“Set-Cookie”。
这可能不是您要找的答案,但我自己试了一下,马上就见效了:
Set-Cookie:SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
Set-Cookie:lang=en-US; Path=/; Domain=example.com
Set-Cookie:JSESSIONID=76A68D96ED044DDFF0CC266810F52DDA; Path=/; HttpOnly
这就是响应的样子。也许这是您的特定 Web 容器或您的实现的问题。
尝试调试应用程序(使用远程调试工具)找出 header 丢失的位置。
像这样直接设置 Cookie 有点尴尬,考虑到 Servlet API 有专门用于处理 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}
根据 RFC https://www.rfc-editor.org/rfc/rfc6265#page-7 允许有两个 header 具有相同的键“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() 没有添加新的 header。根据此方法的 javadoc-
Adds a response header with the given name and value. This method allows response headers to have multiple values.
所以似乎允许多个值,但我不确定如何在 servlet 响应中使用多个“Set-Cookie”。
这可能不是您要找的答案,但我自己试了一下,马上就见效了:
Set-Cookie:SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
Set-Cookie:lang=en-US; Path=/; Domain=example.com
Set-Cookie:JSESSIONID=76A68D96ED044DDFF0CC266810F52DDA; Path=/; HttpOnly
这就是响应的样子。也许这是您的特定 Web 容器或您的实现的问题。
尝试调试应用程序(使用远程调试工具)找出 header 丢失的位置。
像这样直接设置 Cookie 有点尴尬,考虑到 Servlet API 有专门用于处理 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}