【问题标题】:Disable JSESSIONID conditionally in Spring-Boot-Web / Tomcat在 Spring-Boot-Web / Tomcat 中有条件地禁用 JSESSIONID
【发布时间】:2021-08-13 22:56:58
【问题描述】:

我正在尝试有条件地禁用 JSESSIONID Cookie 的创建。

如果请求中存在某个 cookie,我只想创建此 cookie。

我正在使用 spring-boot-starter-web 2.5.3spring-boot-starter-security

到目前为止我已经尝试过什么

Spring-Session -> CookieSerializer Bean

我尝试添加spring-session 并定义一个自定义DefaultCookieSerializer-Bean。原来JSESSIONID实际上并不是来自spring,而是来自底层的Tomcat。

使用 javax.servlet.Filter 阻止设置 Set-Cookie 标头:

package xxxx.configuration;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

@Configuration
public class CookieConsentConfiguration {

    private static class CookieConsentFilter implements Filter {

        private final ObjectMapper objectMapper;

        private CookieConsentFilter(ObjectMapper objectMapper) {
            this.objectMapper = objectMapper;
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            final HttpServletRequest req = (HttpServletRequest) request;

            if (hasCookieConsent(req)) {
                chain.doFilter(request, response);
            } else {
                chain.doFilter(request, new NoCookiesResponseWrapper((HttpServletResponse) response));
            }
        }

        private boolean hasCookieConsent(HttpServletRequest request) {
            try {
                final Cookie[] cookies = request.getCookies();
                if (cookies != null) {
                    for (Cookie cookie : cookies) {
                        if (cookie.getName().equals("cookie_consent_level")) {
                            final String value = cookie.getValue();
                            final JsonNode json = this.objectMapper.readTree(URLDecoder.decode(value, StandardCharsets.US_ASCII));

                            return json.hasNonNull("strictly-necessary") && json.get("strictly-necessary").booleanValue();
                        } else if (cookie.getName().equals("cookie_consent_user_accepted")) {
                            return Boolean.parseBoolean(cookie.getValue());
                        }
                    }
                }
            } catch (Exception e) {
                return false;
            }

            return false;
        }
    }

    private static class NoCookiesResponseWrapper extends HttpServletResponseWrapper {

        public NoCookiesResponseWrapper(HttpServletResponse response) {
            super(response);
        }

        @Override
        public void addCookie(Cookie cookie) {
            // not allowed
        }

        @Override
        public void setHeader(String name, String value) {
            if (!name.equalsIgnoreCase("set-cookie")) {
                super.setHeader(name, value);
            }
        }

        @Override
        public void addHeader(String name, String value) {
            if (!name.equalsIgnoreCase("set-cookie")) {
                super.addHeader(name, value);
            }
        }
    }

    @Bean
    public Filter cookieConsentFilter(ObjectMapper objectMapper) {
        return new CookieConsentFilter(objectMapper);
    }
}

但这也没有用。 JSESSIONID 仍在创建中。

如果不满足某些条件,是否有任何方法可以禁用创建此(或任何)Cookie?

【问题讨论】:

  • 如果您出于任何正当理由(例如登录)使用会话,则会话 cookie 是最必要的。
  • 我知道,而且我有登录信息。但是 Session-Cookie 是在第一次访问页面时创建的,根本没有尝试登录。此外,我已将 SessionCreationPolicy 设置为 IF_REQUIRED。使用会话的服务器端是否也记住了 CSRF 值?这些可能是创建会话的原因吗?
  • 默认使用LazyCsrfTokenRepository(参见this question),因此除非您使用令牌,否则不会生成会话。您能否提供一个导致问题的页面的最小示例?
  • 是的,这就是问题所在。我在每个百里香模板中都使用了csrf 标记。这迫使会话被创建
  • @Felix:你能回答你的问题吗?

标签: java spring-boot tomcat


【解决方案1】:
免责声明:此答案不直接回答问题,即如何停止根据条件创建 JSESSIONID。由于这个原因,我不会接受这个答案。

GDPR

我发现默认情况下允许严格必要的 cookie,而无需向用户显示 cookie 层之前将 cookie 发送到客户端。据我了解,您仍然必须在第一页印象中向用户显示 cookie 层。

据我了解,这实质上意味着您可以在用户首页加载时发送Set-Cookie-Headers 以获取绝对必要的cookie。在该页面上,必须显示 cookie 层,但此时可能已经存在绝对必要的 cookie。

欲了解更多信息,请参阅:https://gdpr.eu/cookies/


技术资料

一般

首先,我们要了解JSESSIONID是在什么情况下创建的。

每当一段代码试图从应用程序服务器获取会话时,就会创建它。这意味着,如果我们能够以一种不尝试从应用服务器获取会话的方式构造我们的代码,则不会将JSESSIONID-Cookie 发送到客户端。

但是有一些陷阱。在某些情况下,会话是所有 cookie 中最必要的:

  • 登录:登录(如网站上的人类用户登录),严格要求会话
  • CSRF-Tokens:服务器需要一些东西来获取这些令牌。这是会议
  • ...更多

Spring-Boot 示例

我在版本2.5.3 中使用spring-boot-starter-web。我希望仅在确实需要时才创建会话,原因之一是我上面提到的:用户尝试登录或用户出于安全原因访问需要创建 CSRF 令牌的站点。

第一个相关配置可以在我的WebSecurityConfigurerAdapter找到: 将SessionCreationPolicy 设置为IF_REQUIRED

    @Configuration
    public static class GlobalWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        public void configure(WebSecurity web) {
            web.ignoring().antMatchers("/webjars/**", "/css/**", "/js/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .csrf()
                        .and()
                    .headers()
                        .frameOptions()
                        .deny()
                        .and()
                    .sessionManagement()// these two lines
                        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                        .and()
                    .oauth2Login()
                        .and()
                    .logout()
                        .logoutSuccessUrl("/");
        }
    }

在我的案例中,第二个相关的事情是仅在页面确实需要 CSRF-Tokens 时嵌入它们。

我有几个百里香模板,我总是使用相同的<head> 部分。在我的每个模板中,我的 <head> 部分都包含以下几行:

    <meta name="_csrf" th:content="${_csrf.token}"/>
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>

虽然这对于执行POSTPUT、...的页面仍然是必需的 - 对我的服务器的请求,但我的大多数页面都不需要它。

我删除了每个 thymeleaf 模板中不需要的 CSRF-Token 的使用。

现在,只有在用户尝试访问包含CSRF-Token 的页面时,才会从应用服务器获取会话(并创建JSESSIONID

【讨论】:

    猜你喜欢
    • 2012-02-13
    • 2016-01-29
    • 2010-11-01
    • 2017-09-08
    • 1970-01-01
    • 2015-10-25
    • 2017-10-30
    • 1970-01-01
    相关资源
    最近更新 更多