在 Spring Security saml2 提供程序版本 5.7.x 中,如果在身份验证响应中提供了 InResponseTo,则引入了强制验证。
验证逻辑期望在 HttpSession 中找到保存的 Saml2AuthenticationRequest。然而,只有在未设置 SameSite 属性的情况下,这才有可能。
根据我当前项目的安全要求,将其设置为宽松或严格。此配置是在应用程序外部完成的。这会导致会话和请求数据丢失。
也许有人已经处理过某个问题并且知道如何处理?我没有看到任何禁用验证的方法或保存库中可用的请求的替代方法。
在将 Spring Security 从 5.x 升级到 6 时,我遇到了 Spring 会话 cookie 的相同问题。对此似乎没有任何标准解决方案。目前,我提供了
Saml2AuthenticationRequestRepository
的实现,它根据 RelayState 参数将 SAML 请求保存在数据库中。以下是使用 Mongo 的示例实现。任何后端(内存缓存、Redis、MySQL 等)都可以使用 -
import java.util.Optional;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.security.saml2.core.Saml2ParameterNames;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.stereotype.Repository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Repository
@RequiredArgsConstructor
@Slf4j
public class MongoSaml2AuthenticationRequestRepository implements Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
public static final String SAML2_REQUEST_COLLECTION = "saml2RequestsRepository";
private final @NonNull MongoTemplate mongoTemplate;
@Override
public AbstractSaml2AuthenticationRequest loadAuthenticationRequest(HttpServletRequest request) {
String relayState = request.getParameter(Saml2ParameterNames.RELAY_STATE);
if (relayState == null) {
return null;
}
log.debug("Fetching SAML2 Authentication Request by relay state : {}", relayState.get());
Query query = Query.query(Criteria.where("relayState").is(relayState.get()));
AbstractSaml2AuthenticationRequest authenticationRequest = mongoTemplate.findOne(query, AbstractSaml2AuthenticationRequest.class, SAML2_REQUEST_COLLECTION);
if (!authenticationRequest.getRelayState().equals(relayState.get())) {
log.error("Relay State received from request '{}' is different from saved request '{}'.", relayState.get(), authenticationRequest.getRelayState());
return null;
}
log.debug("SAML2 Request retrieved : {}", authenticationRequest);
return authenticationRequest;
}
@Override
public void saveAuthenticationRequest(AbstractSaml2AuthenticationRequest authenticationRequest,
HttpServletRequest request, HttpServletResponse response) {
//As per OpenSamlAuthenticationRequestResolver, it will always have value. However, one validation can be added to check for null and regenerate.
String relayState = authenticationRequest.getRelayState();
log.debug("Relay State Received: {}", relayState);
mongoTemplate.save(authenticationRequest, SAML2_REQUEST_COLLECTION);
}
@Override
public AbstractSaml2AuthenticationRequest removeAuthenticationRequest(HttpServletRequest request,
HttpServletResponse response) {
AbstractSaml2AuthenticationRequest authenticationRequest = loadAuthenticationRequest(request);
if (authenticationRequest == null) {
return null;
}
mongoTemplate.remove(authenticationRequest, SAML2_REQUEST_COLLECTION);
return authenticationRequest;
}
}
添加此后,所有 SAML 验证(包括 InResponseTo 验证)均已成功通过。由于 RelayState 不应该根据规范在 SAML 请求之间更改,因此这似乎是合理的替代方案(到目前为止还没有任何标准解决方案)。我正在通过标准安全测试来运行它,并将用我的发现更新解决方案(如果有)。
我有同样的问题,我尝试了一些方法,例如设置 sameSite=None,但它不起作用