我创建了一个端点“/api/chat”,当被调用时,它会向 OpenAI API 发出另一个请求(付费且 API 密钥有效,所有这些)。在 Postman 中测试端点时,我首先请求 /auth/login 端点获取有效的 JWT 登录令牌,将其复制到 /api/chat 调用的授权标头,然后发送它(登录身份验证令牌 100% 有效) ,并且也适用于所有其他端点)。
当向 /api/chat 发出请求时,Postman 大约需要 1.5-2 秒来处理它。在第一秒,Intellij 中的运行控制台会记录正确的内容以表明正在发出请求。在剩余时间内,它会记录来自 OpenAI API 模型的确切预期响应。你可能会认为这意味着一切都执行得很好,而且看起来确实如此,但是 Postman(以及我的网站,如果你在那里尝试的话)返回 403 而不是 200 OK。这是一个问题,因为即使我可以实际看到 IDE 控制台中记录的正确响应没有错误,但 403 意味着我无法提取任何内容以供使用,并且网站无法接收它并将其视为请求完全失败。
我“修复”此问题的唯一方法是在我的 SecurityConfig 文件中公开 /api/chat 端点,这使其以完全相同的方式执行并给出相同的结果,但按预期返回 200 OK。但是,我当然不能这样保留它,因为这意味着人们可以公开访问端点,而每个请求都会花费我的费用。
我花了很多时间试图解决这个问题,包括谷歌搜索、询问 GPT - 什么也没有。验证了我所有的 JWT 身份验证内容,尝试了始终保留身份验证令牌的方法,但没有任何效果。 CORS 和 CSRF 之类的东西,似乎也不是。我只能认为它与 Spring Security 有关,因为将端点添加到 SecurityConfig 中的 .permitAll() 列表(使其公开)是唯一使其按预期返回 200 OK 的事情。这是非常令人沮丧的,因为它准确地执行和记录了它应该如何以及应该做什么,但只是出于某种原因返回 403。
这是我使用 Spring 的第一个项目,所以如果它是微不足道的,那么请帮忙。提前致谢。 API密钥在我的环境变量中设置,尽管我已经包含了一些错误日志记录,但没有记录任何错误,只有我期望从AI模型中得到的正确响应,然后它返回403,因为我已经提及。我将包含相关的代码位,但如果您认为问题出在另一个文件或这些文件的一部分中,请告诉我,我将尽我所能分享。我认为这并不是太重要,而是我正在使用 React 作为前端。
:
安全配置:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/auth/**", "/", "/index.html", "/manifest.json", "/static/**", "/*.js", "/*.jsx", "/*.css", "/home", "/log-in", "/sign-up")
.permitAll()
.requestMatchers("/auth/signup", "/auth/login").anonymous()
.anyRequest()
.authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
控制器:
@PostMapping
public Mono<ResponseEntity<String>> getChatCompletion(@RequestBody ChatRequest chatRequest, @RequestHeader HttpHeaders headers) {
return openAiService.getChatCompletion(chatRequest.getInput())
.map(response -> {
logger.info("Response: {}", response); // Logging response
return ResponseEntity.ok(response);
})
.defaultIfEmpty(ResponseEntity.noContent().build());
}
public static class ChatRequest {
private String input;
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
}
服务:
public Mono<String> getChatCompletion(String userInput) {
String requestBody = String.format("""
{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "MY CONTENT"
},
{
"role": "user",
"content": "%s"
}
],
"temperature": 1,
"max_tokens": 256,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0
}
""", userInput);
logger.info("Sending request to OpenAI with body: {}", requestBody);
return this.webClient.post()
.uri("/chat/completions")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + openaiApiKey)
.bodyValue(requestBody)
.retrieve()
.bodyToMono(String.class)
.doOnNext(response -> logger.info("Received response from OpenAI: {}", response))
.doOnError(WebClientResponseException.class, error -> {
logger.error("Error response from OpenAI: {}", error.getResponseBodyAsString());
})
.doOnError(error -> logger.error("Error occurred: ", error));
}
我的邮递员请求(不太相关,这不是问题):
{
input: "MY INPUT"
}
我今天遇到了同样的奇怪行为:安全端点被调用,但在执行后返回 403 状态。
更糟糕的是,使用 MockMvc 的 JUnit 集成测试不会产生 403 状态。
拥有另一个工作端点给了我一个关于正在发生的事情的提示:
我的代码中的返回类型没有用
@ResponseBody
注释,在 Spring Boot 3.3.2 中,它在方法完成后显然会抛出异常。然后这个异常会被 Spring Security 变成 403。
(我最终将注释添加到我的
void
方法中。🤯)