浏览器不存储 JSESSIONID cookie

问题描述 投票:0回答:1

我正在尝试使用 Spring Security 以及用户名和密码凭据在我的 REST API 应用程序中编写身份验证。

我有自定义控制器,可将用户名和密码令牌保存到 SecurityContext。该 SecurityContext 保存到 SecurityContextRepository。

当我使用 Postman 登录应用程序然后使用其他 api 端点时,它会成功完成。但是当我尝试在浏览器中执行相同操作时,成功登录后使用 api 端点收到 401 错误。

我注意到:登录端点响应设置标头

Set-Cookie: JSESSIONID=...
,Postman 在使用其他 api 端点时使用它来发送。我在浏览器中看到该标头,但浏览器不存储 cookie(我已在开发工具中检查过)

登录端点的响应标头示例

access-control-allow-credentials: true
access-control-allow-origin: http://localhost:3000
cache-control: no-cache, no-store, max-age=0, must-revalidate
connection: keep-alive
content-length: 0
date: Mon, 30 Dec 2024 18:28:02 GMT
expires: 0
keep-alive: timeout=60
pragma: no-cache
set-cookie: JSESSIONID=E2AE75224A50A04D4E64790FA8C2AE46; Path=/; HttpOnly
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 0

首先,我设置了 CORS 配置以允许使用任何标头、方法和凭据。但这并没有帮助。

我尝试根据响应手动创建 cookie,但每个下一个请求都返回 401 错误。

我尝试过配置 cookie secure 和 http-only 属性,但也没有帮助。

我尝试使用不同的浏览器,但结果相同。

我还向 React 应用程序的请求添加了

withCredentials: true
标头,但没有帮助

应用程序使用相同的域(localhost)但不同的端口(spring:8080,react:3000)

CampaignList.jsx

import { List } from "antd";
import axios from "axios";
import { useLoaderData } from "react-router";

export const loader = async () => {
    const url = "http://localhost:8080/api/campaigns/get";
    try {
        const response = await axios.get(url, {
            headers: {
                withCredentials: true,
            },
        });
        console.log(response);

        return response.data;
    } catch (err) {
        alert(err.message);
    }
};

const CampaignList = () => {
    const campaigns = useLoaderData();

    return (
        <List
            header={<div>Кампании</div>}
            dataSource={campaigns}
            renderItem={(item) => (
                <List.Item>
                    <a href={item}>{item.name}</a>
                </List.Item>
            )}
        />
    );
};

export default CampaignList;

SpringConfig.java

@EnableWebSecurity(debug = true)
@RequiredArgsConstructor
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {
    @Autowired
    private CorsConfigurationSource corsConfigurationSource;

    @Autowired
    private SecurityContextRepository securityContextRepository;

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .cors(
                        cors -> cors
                        .configurationSource(corsConfigurationSource)
                )
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(requests -> requests
                        .requestMatchers("/api/users/create", "/auth/login", "/error").permitAll()
                        .anyRequest().authenticated()
                )
                .securityContext(securityContext -> securityContext
                        .securityContextRepository(securityContextRepository)
                        .requireExplicitSave(true)
                )
                .httpBasic(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public StrictHttpFirewall httpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        firewall.setAllowUrlEncodedDoubleSlash(true);
        return firewall;
    }
}

CorsConfig.java

@Configuration
public class CorsConfig {
    @Bean
    public UrlBasedCorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("http://localhost:3000/"));
        configuration.setAllowedMethods(List.of("*"));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

AuthenticationController.java

@RestController
@RequestMapping("/auth")
public class AuthenticationController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private SecurityContextRepository securityContextRepository;

    @PostMapping("/login")
    public ResponseEntity<?> login(
            @RequestBody SignInRequest signInRequest,
            HttpServletRequest request,
            HttpServletResponse response) {
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(signInRequest.getUsername(), signInRequest.getPassword())
        );
        context.setAuthentication(authentication);
        SecurityContextHolder.setContext(context);
        securityContextRepository.saveContext(context, request, response);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

SecurityContextRepositoryConfig.java

@Configuration
public class SecurityContextRepositoryConfig {
    @Bean
    public SecurityContextRepository securityContextRepository() {
        return new HttpSessionSecurityContextRepository();
    }
}
spring authentication spring-security session-cookies jsessionid
1个回答
0
投票

错误是我使用 withCredentials 作为标头。但它是财产。我在

index.js
这个属性默认设置为
true

axios.defaults.withCredentials = true;
© www.soinside.com 2019 - 2024. All rights reserved.