防止重定向到 /error 的未经授权的 http 请求设置 session cookie - spring boot - spring security

Prevent unauthorized http requests redirected to /error from setting session cookie - spring boot - spring security

上下文

我的应用程序遇到了一些问题。我们正在使用 Spring Boot 2.4.10 和 Spring Security 5.4.8。我们使用 cookie 与应用程序交互。

我们有一个前端应用程序存储在 src/main/resources 中,除其他外,它连接到 /api/v1/notification 中公开的 websocket 端点。

我的配置

application.properties 文件:

# cookie-related settings
server.servlet.session.cookie.name=mySessionCookie
server.servlet.session.cookie.path=/
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  private final String[] authorizedWithoutAuth = {
      "/",
      "/index.html",
      "/static/**"
  };

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic()
        .and()
          .authorizeRequests()
          .antMatchers(authorizedWithoutAuth).permitAll()
          .anyRequest().authenticated()
        .and()
          .csrf().disable()
        .and()
        .logout().logoutUrl("/api/v1/logout").invalidateHttpSession(true)
        .deleteCookies("mySessionCookie");
  }
}

问题

当没有用户登录时,前端尝试定期访问 websocket 端点以打开连接。

第一个 api/v1/notification 结束重定向到 /error 端点,returns 一个 HttpServletResponse 和 'Set-Cookie' header (我认为这可能是第一个请求中设置的匿名 cookie?)和 401 状态。我无法改变这种行为。

以下对 api/v1/notification 的请求在 header 中使用此 cookie(当用户未登录时)。这些请求也被重定向到 /error 端点,该端点 returns 每次 HttpServletResponse 具有 401 状态但这里 no 'Set-Cookie' header 已包含。

一旦用户使用授权 headers 登录,响应就会设置正确的 cookie,并在以下请求中使用。

问题是,有时设置的 cookie 突然再次变为无效的 cookie,并且使用这个新的无效 cookie 完成的以下请求会变成登录页面的重定向。

检查代码后,似乎有一个旧的 api/v1/notification 请求(在登录请求之前)发生,带有无效的 cookie(匿名的,在登录之前存在)。

此请求被重定向到 /error 端点:这里,HttpServletResponse 再次具有 401 状态并且 包含Set-Cookie header 即修改浏览器cookie(替换好的)。

以下是问题的方案,希望能让它更容易理解。

预期行为

我想防止未经授权的请求设置 session cookie。

如果之前的请求以 401 代码响应,那没关系,但我不希望它更改当前设置的 cookie。

我试过了...

感谢大家抽空。抱歉 post 长度 :)

我开始挖掘 Spring 安全库,并注意到 session cookie 是在 HttpSessionRequestCache.saveRequest(...) 方法中设置的:

public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
  if (!this.requestMatcher.matches(request)) {
    if (this.logger.isTraceEnabled()) {
      this.logger.trace(LogMessage.format("Did not save request since it did not match [%s]", this.requestMatcher));
    }
  } else {
    DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, this.portResolver);
    if (!this.createSessionAllowed && request.getSession(false) == null) {
      this.logger.trace("Did not save request since there's no session and createSessionAllowed is false");
    } else {
      request.getSession().setAttribute(this.sessionAttrName, savedRequest);
      if (this.logger.isDebugEnabled()) {
        this.logger.debug(LogMessage.format("Saved request %s to session", savedRequest.getRedirectUrl()));
      }
    }
  }
}

创建DefaultSavedRequestobject时出现'Set-Cookie'header。 我将 WebSecurityConfig 更改为以下内容:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  ...

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic()
        .and()
          .requestCache().requestCache(getHttpSessionRequestCache()) // This is new
        .and()
          .authorizeRequests().antMatchers(authorizedWithoutAuth).permitAll()
          .anyRequest().authenticated()
        .and()
          .csrf().disable()
        .and()
          .logout().logoutUrl("/api/v1/logout").invalidateHttpSession(true)
          .deleteCookies("mySessionCookie");
  }


  public HttpSessionRequestCache getHttpSessionRequestCache()
  {
    HttpSessionRequestCache httpSessionRequestCache = new HttpSessionRequestCache();
    httpSessionRequestCache.setCreateSessionAllowed(false); // I modified this parameter 
    return httpSessionRequestCache;
  }
}

现在可以了。使用授权 headers 登录时,cookie 设置正确,但所有带有无效 session cookie 或过期 return 的请求都没有设置 401 响应一个新的。

另外,看了this answer之后,我更明白这个createSessionAllowed在做什么