Reading cookies using @CookieValue
Annotation @CookieValue allows a handler method parameter to be mapped to the value of an Http Cookie.
Definition of CookieValueVersion: 7.0.6 package org.springframework.web.bind.annotation;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
@AliasFor("name")
String value() default ""; 1
@AliasFor("value")
String name() default ""; 2
boolean required() default true; 3
String defaultValue() default ValueConstants.DEFAULT_NONE; 4
}
Reading Cookie Value
...
import org.springframework.web.bind.annotation.CookieValue;
...
@RequestMapping
public String handleRequest (
@CookieValue(value = "myCookieName",
defaultValue = "defaultCookieValue")
String cookieValue, Model model) {
System.out.println(cookieValue);
......
return "my-page";
}
Like other scenarios, there's an automatic value type conversion. In above example the cookie value is mapped to String type.
Writing Cookies using HttpServletResponse
To write cookies we can use javax.servlet.http.HttpServletResponse:
...
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
...
@RequestMapping
public String handleRequest (HttpServletResponse response, Model model) {
Cookie newCookie = new Cookie("testCookie", "testCookieValue");
newCookie.setMaxAge(24 * 60 * 60);
response.addCookie(newCookie);
.......
return "my-page";
}
Writing Cookies using Spring 5+ ResponseCookie
package com.logicbig.example;
.....
import org.springframework.http.ResponseCookie;
@Controller
public class MyController {
@RequestMapping("test")
public String handleTestRequest(Model model,
HttpServletRequest request,
HttpServletResponse response) {
ResponseCookie responseCookie = ResponseCookie.from("testCookie")
.value("testCookieValue")
.maxAge(Duration.ofDays(30))
.build();
response.addHeader("Set-Cookie", responseCookie.toString());
model.addAttribute("msg", "test method msg");
return "my-page";
}
.............
}
Using HttpServletRequest to read Cookies:
Instead of using @CookieValue we can also use HttpServletRequest as handler parameter to iterate through or read cookie values.
.....
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
....
@RequestMapping
public String handleTestRequest (Model model,
HttpServletRequest request){
Cookie[] cookies = request.getCookies();
if (cookies != null) {
Arrays.stream(cookies)
.forEach(c -> System.out.println(c.getName() + "=" + c.getValue()));
}
.......
return "my-page";
}
In above example we can also use the static helper method to find a specific cookie: org.springframework.web.util.WebUtils#getCookie(HttpServletRequest request, String name)
Cookie Security Attributes
As of Spring 6 / Jakarta EE 9, these three attributes are considered table stakes for any production cookie. Use ResponseCookie to set them — the raw jakarta.servlet.http.Cookie API cannot express SameSite at all.
-
HttpOnly — prevents JavaScript from reading the cookie via document.cookie. Blocks the most common XSS-based session-hijacking vector. Should be on by default for any session or auth cookie.
-
Secure — instructs the browser to transmit the cookie only over HTTPS. Without this, the cookie is sent in plaintext over HTTP and is trivially intercepted. Always set in production; can be relaxed to false in local development only.
-
SameSite — controls whether the browser sends the cookie on cross-site requests. Two practical values:
SameSite=Strict — cookie is never sent on any cross-site request, including navigations from external links. Strongest CSRF protection, but breaks OAuth and SSO redirect flows that land the user on your domain from an external one.
SameSite=Lax — cookie is sent on top-level navigations (e.g. clicking a link) but not on sub-resource requests (images, iframes, fetch). A safe default for most apps; supported by all modern browsers.
SameSite=None — cookie is always sent cross-site. Requires Secure to be set or browsers will reject it. Use only for deliberate cross-site scenarios such as embedded widgets or third-party APIs.
A minimal secure cookie using ResponseCookie:
ResponseCookie cookie = ResponseCookie.from("testCookie", value)
.httpOnly(true)
.secure(true)
.sameSite("Lax")
.path("/")
.maxAge(Duration.ofHours(1))
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
Running Example
Run the web application by using embedded tomcat:
mvn clean jetty:run
Output in Browser
Unit Tests
package com.logicbig.example;
import jakarta.servlet.http.Cookie;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringJUnitConfig(classes = MyWebConfig.class)
@WebAppConfiguration
public class ControllerTest {
private final MockMvcTester mockMvcTester;
@Autowired
public ControllerTest(WebApplicationContext wac) {
this.mockMvcTester = MockMvcTester.from(wac);
}
@Test
void testExpectedCookie() {
// Asserting a cookie returned by the server
assertThat(mockMvcTester.get().uri("/test"))
.hasStatusOk()
.cookies()
.hasValue("testCookie","testCookieValue" );
}
@Test
void testCookieRequest() {
String cookieValue = "myUnitTestCookie";
Cookie requestCookie = new Cookie("testCookie", cookieValue);
// Sending a cookie and asserting a Model attribute
assertThat(mockMvcTester.get().uri("/test2")
.cookie(requestCookie))
.hasStatusOk()
.model()
.containsEntry("cookieValue", cookieValue);
}
}
mvn test -Dtest="ControllerTest" Output$ mvn test -Dtest="ControllerTest" [INFO] Scanning for projects... [INFO] [INFO] ------------< com.logicbig.example:spring-cookie-handling >------------- [INFO] Building spring-cookie-handling 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-cookie-handling --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-cookie-handling\src\main\resources [INFO] [INFO] --- compiler:3.15.0:compile (default-compile) @ spring-cookie-handling --- [INFO] Nothing to compile - all classes are up to date. [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-cookie-handling --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-cookie-handling\src\test\resources [INFO] [INFO] --- compiler:3.15.0:testCompile (default-testCompile) @ spring-cookie-handling --- [INFO] Nothing to compile - all classes are up to date. [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-cookie-handling --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.ControllerTest myUnitTestCookie [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.061 s -- in com.logicbig.example.ControllerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.091 s [INFO] Finished at: 2026-03-18T16:06:03+08:00 [INFO] ------------------------------------------------------------------------ INFO: Completed initialization in 2 ms INFO: Completed initialization in 2 ms
Example ProjectDependencies and Technologies Used: - spring-webmvc 7.0.6 (Spring Web MVC)
Version Compatibility: 3.2.3.RELEASE - 7.0.6 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- spring-test 7.0.6 (Spring TestContext Framework)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- assertj-core 3.26.3 (Rich and fluent assertions for testing in Java)
- junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
- JDK 25
- Maven 3.9.11
|