Spring Boot中HttpOnly Cookie未设置且浏览器不向后端发送的问题
问题背景
尝试将Spring Boot应用从local storage迁移至HttpOnly Cookie实现安全防护,但遇到以下核心问题:
- 响应头存在
Set-Cookie字段,但浏览器未设置Cookie(即使将HttpOnly设为false也无法在浏览器中看到) - 后续接口请求中
request.getCookies()始终为null,导致接口返回401未授权
相关代码片段
1. CookieUtil工具类
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; public class CookieUtil { public static void create(HttpServletResponse httpServletResponse, String name, String value, Integer maxAge) { Cookie cookie = new Cookie(name, value); cookie.setSecure(false); cookie.setHttpOnly(true); cookie.setMaxAge(maxAge); cookie.setPath("/"); cookie.setDomain("localhost"); httpServletResponse.addCookie(cookie); } public static void clear(HttpServletResponse httpServletResponse, String name) { Cookie cookie = new Cookie(name, null); cookie.setPath("/"); cookie.setHttpOnly(true); cookie.setMaxAge(0); httpServletResponse.addCookie(cookie); } }
测试时将
HttpOnly设为false,浏览器仍未显示该Cookie;setSecure已设为false适配本地开发环境
2. 登录接口方法
public AccessTokenResponse signIn(LoginDto loginDto) { AccessTokenResponse accessTokenResponse = authorizations.signIn(loginDto); String jwt= accessTokenResponse.getToken(); int maxAge= (int)accessTokenResponse.getExpiresIn(); CookieUtil.create(response, "accessToken", jwt, maxAge); return accessTokenResponse; }
使用Keycloak作为身份管理器,能正常获取token,响应头存在
Set-Cookie字段,但浏览器未完成Cookie设置
3. 获取员工信息接口
@GetMapping("/employee") public ResponseEntity<EmployeeDto> getEmployeeByKeycloakId(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); String token = null; if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals("accessToken")) { token = cookie.getValue(); break; } } } if (token == null) { return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } String userid= tokenService.getUserIdFromToken(token); return new ResponseEntity<>( employeeMapper.toEmployee(this.employeeUseCases.getByKeycloakId(userid)), HttpStatus.OK ); }
接口始终返回401,
request.getCookies()为null,说明浏览器未向后端发送Cookie
4. 前端代码
登录请求
signIn(user: User, rememberMe: boolean): Observable<any> { return this._httpClient.post(`${this._apiUrl}/login`, user).pipe( switchMap((response: any) => { if (response.access_token) { if(rememberMe){ this._storageService.setItem('rememberMe', JSON.stringify(rememberMe)); } } this._authenticated = true; return this._userService.get(); }) ); }
获取员工信息请求(已设置withCredentials)
get(): any { return this._httpClient .get<User>(`${this._apiUrl}/employee`, { withCredentials: true }) .pipe( tap((user) => { console.log(user) this._user.next(user); }) ); }
后端跨域配置
@CrossOrigin(origins = "http://localhost:4200", allowCredentials = "true") @RestController @RequestMapping("/api/v1/employees") public class EmployeeController @CrossOrigin(origins = "http://localhost:4200", allowCredentials = "true") @RestController @RequestMapping("/api/v1/auth") public class AuthorisationController {
解决方案
1. 修正Cookie的Domain配置
本地开发时,后端运行在localhost:8080、前端在localhost:4200,设置cookie.setDomain("localhost")会触发浏览器的同源校验逻辑(不同端口属于不同源,部分浏览器对localhost的Domain处理有特殊限制)。直接移除cookie.setDomain("localhost"),让浏览器自动绑定当前请求的域名,确保Cookie能被正确识别。
2. 登录请求添加withCredentials: true
前端登录请求是触发Set-Cookie的核心请求,必须开启withCredentials才能让浏览器接收跨域返回的Cookie。修改登录请求代码:
signIn(user: User, rememberMe: boolean): Observable<any> { return this._httpClient.post(`${this._apiUrl}/login`, user, { withCredentials: true }).pipe( switchMap((response: any) => { if (response.access_token) { if(rememberMe){ this._storageService.setItem('rememberMe', JSON.stringify(rememberMe)); } } this._authenticated = true; return this._userService.get(); }) ); }
3. 验证Cookie的MaxAge参数
确认Keycloak返回的expiresIn转换为int后是正数,避免因MaxAge为负数或0导致Cookie立即过期。
4. 全局配置跨域(可选但更可靠)
替代控制器层面的@CrossOrigin,全局配置跨域确保所有请求都能正确处理:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:4200") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }
5. 浏览器调试验证
- 在开发者工具的Application > Cookies面板查看是否存在
accessToken(HttpOnly为true时不会在document.cookie中显示,但会在此面板列出) - 检查登录请求的响应头,确认
Set-Cookie字段格式正确,无语法错误 - 查看员工信息请求的请求头,确认是否携带
Cookie字段
内容的提问来源于stack exchange,提问作者Oussema Sahbeni
相关产品推荐
相关产品推荐

