Spring Security 로그인 401 Unauthorized 오류

채정윤·2025년 5월 14일

Error

목록 보기
8/9

✅ 🔧 해결 방향 설정

🔁 선택한 방법: formLogin() 제거하고 API 방식으로 처리 (RESTful 대응)

  • formLogin()기본 로그인 페이지를 사용하는 방식
    → 우리가 사용하는 프론트엔드 커스텀 로그인과는 충돌
  • 따라서 formLogin() 제거 후, 직접 로그인 API를 만들고 JWT 발급 방식으로 전환

✅ 🔐 SecurityConfig 수정 요약

@Bean
public SecurityFilterChain filterChain(HttpSecurity http, CorsConfigurationSource corsConfigurationSource) throws Exception {
    http
        .csrf(csrf -> csrf.disable())
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/auth/login", "/api/auth/register").permitAll()
            .anyRequest().authenticated()
        )
        .logout(logout -> logout
            .logoutUrl("/api/auth/logout")
            .logoutSuccessHandler((request, response, authentication) -> {
                response.setStatus(HttpStatus.OK.value());
            })
        )
        .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                UsernamePasswordAuthenticationFilter.class);

    return http.build();
}

✅ 👤 로그인 API 흐름 정리

🔹 1) @RestController 작성

@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
    log.info("로그인 요청: Email={}", request.getEmail());
    LoginResponse loginResponse = authService.login(request);
    log.info("로그인 결과: {}", loginResponse);
    return ResponseEntity.ok(loginResponse);
}

🔹 2) 로그인 로직 (authService.login())

  • 이메일로 사용자 조회 → 비밀번호 확인 → JWT 발급 → 로그인 성공 여부 반환
@Transactional
public LoginResponse login(LoginRequest request) {
    Optional<User> optionalUser = userRepository.findByEmail(request.getEmail());

    if (optionalUser.isEmpty()) return 실패 응답;

    User user = optionalUser.get();

    if (!passwordEncoder.matches(request.getPassword(), user.getPassword()))
        return 실패 응답;

    // JWT 생성
    String token = jwtTokenProvider.createToken(user.getEmail(), List.of(user.getRole().name()));

    return 성공 응답;
}

✅ 🔎 프론트엔드 로그인 코드 확인

🔹 React 로그인 버튼 클릭 시 동작

const handleLogin = async() => {
  try {
    const response = await login({ email, password }); // axios POST 요청
    console.log(response); // JWT 응답 확인
  } catch(err) {
    console.log(`로그인 실패 : ${err}`);
    alert("로그인 실패");
  }
}

🔹 axios 요청 구조

export const login = async(data: LoginRequest): Promise<LoginResponse> => {
    return (await axios.post('/auth/login', data)).data;
}

✅ 반드시 알아야 할 개념 요약

🔐 1. Spring Security 기본 로그인 (formLogin)

  • 브라우저 기반 폼 로그인 페이지 자동 제공
  • 우리가 사용하는 API 방식에는 맞지 않음

🔑 2. API 로그인 방식

  • 클라이언트가 직접 로그인 API 호출 (/api/auth/login)
  • JWT를 응답으로 받아 클라이언트가 Authorization 헤더에 넣어 요청

🪪 3. JWT (JSON Web Token)

  • 사용자의 인증 정보를 담은 서명된 토큰
  • 프론트에서 Authorization: Bearer <token> 형태로 헤더에 추가

🛡️ 4. JwtAuthenticationFilter

  • 클라이언트 요청이 들어올 때마다 JWT가 있는지 확인 → 인증 객체 생성

0개의 댓글