如何在登录 Spring Boot 2.1.0 时禁用 Set-Cookie header 上的 HttpOnly 标志
How to disable HttpOnly flag on Set-Cookie header on login in Spring Boot 2.1.0
我在 set-cookie header 上禁用 httpOnly 标志时遇到问题。这主要是在响应中发回 JSESSIONID 时的登录问题。请注意,这是在部署在 AWS EBS 上的 tomcat 服务器上。
下面的任何配置在本地都可以正常工作,但在部署时却不行。
我尝试了以下解决方案,none 似乎有效
application.yml 配置
server:
servlet:
session:
cookie:
http-only: false
Servlet 上下文初始化程序
@Bean
open fun servletContextInitializer(): ServletContextInitializer {
return ServletContextInitializer { servletContext ->
servletContext.setSessionTrackingModes(setOf(SessionTrackingMode.COOKIE))
val sessionCookieConfig = servletContext.sessionCookieConfig
sessionCookieConfig.isHttpOnly = false
}
WebServerFactoryCustomizer
@Bean
open fun tomcatCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { tomcat ->
tomcat
.addContextCustomizers(TomcatContextCustomizer { context -> context.useHttpOnly = false })
}
web.xml
<session-config>
<cookie-config>
<http-only>false</http-only>
</cookie-config>
</session-config>
样品请求Header
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer:
Authorization: Bearer null
Content-Type: application/json
Content-Length: 58
Origin:
Connection: keep-alive
TE: Trailers
响应示例Header
HTTP/2.0 200 OK
date: Sat, 16 Mar 2019 14:11:58 GMT
set-cookie: AWSALB=qBpX9uFjtkP4H7gyJ3EXL8na0a7aARiEN/twi0cc2sPywvbysKXXaNfQbe8HaS5hcC6VRnkp09VYj0pGcXiHbWRod9OithDlQ0ZIvHSbY7B5xiJT1r8N+lcRdCcp; Expires=Sat, 23 Mar 2019 14:11:57 GMT; Path=/
server: Apache/2.4.37 (Amazon) OpenSSL/1.0.2k-fips
vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
access-control-allow-origin:
access-control-allow-credentials: true
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
strict-transport-security: max-age=31536000 ; includeSubDomains
x-frame-options: DENY
set-cookie: JSESSIONID=70F12355ABFDD0F42292D9F6CEAA22BF; Path=/; Secure; HttpOnly
X-Firefox-Spdy: h2
我终于能够通过创建一个作为 Spring 安全性的一部分运行的过滤器来解决它。过滤器在 SecurityContextPersistenceFilter 之前执行,因此等到添加 set-cookie header 然后更新 headers(在链中之前,在 doFilter() 执行之后获取最后一次调用)。
过滤器实施
package com.zambezii.app.security.filter
import org.springframework.web.filter.GenericFilterBean
import java.io.IOException
import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class SessionFilter : GenericFilterBean() {
@Throws(IOException::class, ServletException::class)
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
val req = request as HttpServletRequest
val res = response as HttpServletResponse
chain.doFilter(req, res)
removeHttpOnlyFlag(res)
}
private fun removeHttpOnlyFlag(res: HttpServletResponse) {
val setCookieHeaderName = "set-cookie"
var setCookieHeader = res.getHeader(setCookieHeaderName)
if (setCookieHeader != null) {
setCookieHeader = setCookieHeader.replace("; HttpOnly", "")
res.setHeader(setCookieHeaderName, setCookieHeader)
}
}
}
安全配置
open class WebSecurityConfig() : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
...
.authenticated()
.and()
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter::class.java)
.addFilterBefore(SessionFilter(), SecurityContextPersistenceFilter::class.java)
我在 set-cookie header 上禁用 httpOnly 标志时遇到问题。这主要是在响应中发回 JSESSIONID 时的登录问题。请注意,这是在部署在 AWS EBS 上的 tomcat 服务器上。
下面的任何配置在本地都可以正常工作,但在部署时却不行。
我尝试了以下解决方案,none 似乎有效
application.yml 配置
server:
servlet:
session:
cookie:
http-only: false
Servlet 上下文初始化程序
@Bean
open fun servletContextInitializer(): ServletContextInitializer {
return ServletContextInitializer { servletContext ->
servletContext.setSessionTrackingModes(setOf(SessionTrackingMode.COOKIE))
val sessionCookieConfig = servletContext.sessionCookieConfig
sessionCookieConfig.isHttpOnly = false
}
WebServerFactoryCustomizer
@Bean
open fun tomcatCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { tomcat ->
tomcat
.addContextCustomizers(TomcatContextCustomizer { context -> context.useHttpOnly = false })
}
web.xml
<session-config>
<cookie-config>
<http-only>false</http-only>
</cookie-config>
</session-config>
样品请求Header
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer:
Authorization: Bearer null
Content-Type: application/json
Content-Length: 58
Origin:
Connection: keep-alive
TE: Trailers
响应示例Header
HTTP/2.0 200 OK
date: Sat, 16 Mar 2019 14:11:58 GMT
set-cookie: AWSALB=qBpX9uFjtkP4H7gyJ3EXL8na0a7aARiEN/twi0cc2sPywvbysKXXaNfQbe8HaS5hcC6VRnkp09VYj0pGcXiHbWRod9OithDlQ0ZIvHSbY7B5xiJT1r8N+lcRdCcp; Expires=Sat, 23 Mar 2019 14:11:57 GMT; Path=/
server: Apache/2.4.37 (Amazon) OpenSSL/1.0.2k-fips
vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
access-control-allow-origin:
access-control-allow-credentials: true
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
strict-transport-security: max-age=31536000 ; includeSubDomains
x-frame-options: DENY
set-cookie: JSESSIONID=70F12355ABFDD0F42292D9F6CEAA22BF; Path=/; Secure; HttpOnly
X-Firefox-Spdy: h2
我终于能够通过创建一个作为 Spring 安全性的一部分运行的过滤器来解决它。过滤器在 SecurityContextPersistenceFilter 之前执行,因此等到添加 set-cookie header 然后更新 headers(在链中之前,在 doFilter() 执行之后获取最后一次调用)。
过滤器实施
package com.zambezii.app.security.filter
import org.springframework.web.filter.GenericFilterBean
import java.io.IOException
import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class SessionFilter : GenericFilterBean() {
@Throws(IOException::class, ServletException::class)
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
val req = request as HttpServletRequest
val res = response as HttpServletResponse
chain.doFilter(req, res)
removeHttpOnlyFlag(res)
}
private fun removeHttpOnlyFlag(res: HttpServletResponse) {
val setCookieHeaderName = "set-cookie"
var setCookieHeader = res.getHeader(setCookieHeaderName)
if (setCookieHeader != null) {
setCookieHeader = setCookieHeader.replace("; HttpOnly", "")
res.setHeader(setCookieHeaderName, setCookieHeader)
}
}
}
安全配置
open class WebSecurityConfig() : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
...
.authenticated()
.and()
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter::class.java)
.addFilterBefore(SessionFilter(), SecurityContextPersistenceFilter::class.java)