防止未经授权的 HTTP 请求被重定向到 /error 并设置会话 cookie - Spring Boot - Spring Security。

7

背景

我在应用程序方面遇到了一些问题。我们使用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端点,该端点返回一个带有'Set-Cookie'标头(我认为这可能是在第一次请求中设置的匿名Cookie?)和401状态的HttpServletResponse。 我无法更改此行为。

以下请求到api/v1/notification使用此cookie在标题中(当用户未登录时)。 这些请求也被重定向到/error端点,每次都返回带有401状态的HttpServletResponse,但是此处不包括没有'Set-Cookie'标头。

一旦用户使用授权标头登录,响应会设置正确的Cookie,并在随后的请求中使用它。

问题是,有时设置的Cookie突然再次更改为无效的Cookie,随后使用此新的无效Cookie进行的下一个请求会转到登录页面。

检查代码后,似乎存在旧的api/v1/notification请求(在登录请求之前)正在进行,其Cookie无效(即在登录之前存在的匿名Cookie)。

此请求被重定向到/error端点:在这里,一次又一次地HttpServletResponse都具有401状态,并且包含修改浏览器Cookie的Set-Cookie标头(替换好的Cookie)。

接下来是问题的图表,希望能更容易理解。

enter image description here

期望行为

我希望防止未经授权的请求设置会话Cookie。

如果先前的请求以401代码响应,则可以,但我不希望它更改当前设置的Cookie。

我尝试过...

  • I tried extending the ErrorController by returning a ResponseEntity with all the headers present in the input HttpServletResponse except for the 'Set-Cookie' header. This doesn't work.

  • I also tried modifying my configuration to disable anonymous requests:

    @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()
              .anonymous().disable()
              .authorizeRequests()
              // .antMatchers(authorizedWithoutAuth).permitAll() I had to remove these from here, and include them in the method below
              .anyRequest().authenticated()
            .and()
              .csrf().disable()
            .and()
              .logout().logoutUrl("/api/v1/logout").invalidateHttpSession(true)
              .deleteCookies("mySessionCookie");
      }
    
      @Override
      public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(authorizedWithoutAuth);
      }
    }
    

    but the session cookie is still set this way too, with 401 requests.

  • I also tried using @ControllerAdvice to handle the exceptions, but these are thrown by Spring Security in the AbstractSecurityInterceptor, as learnt in this response.

感谢大家抽出时间阅读。对于帖子长度,非常抱歉 :)
1个回答

3

我开始深入研究Spring Security库,并注意到会话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()));
      }
    }
  }
}

在创建DefaultSavedRequest对象时,会出现“Set-Cookie”头信息。 我将我的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;
  }
}

现在它已经可以工作了。使用授权头登录时,cookie被正确设置,但是所有具有无效会话cookie或过期cookie的请求都会返回401响应,而不会设置新的cookie。

此外,在阅读这个答案后,我更好地理解了createSessionAllowed的作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接