프론트엔드에서 JWT 토큰 인증을 구현하기

최인호·2025년 3월 18일

Feign Client를 통한 백엔드 통신 방식

1. 인터페이스 정의

java
@FeignClient(name = "authAdaptor", url = "${api.blog.url}", path = "/api/auth")
public interface AuthAdaptor {
    @PostMapping("/login")
    ResponseEntity<String> login(@RequestBody Map<String, String> loginRequest);
}
  • 백엔드 API에게 부탁할 일의 목록과 같음
    • "나는 /api/auth/login으로 POST 요청을 보낼 거야"
    • "이메일과 비밀번호를 JSON으로 보낼 거야"
    • "그리고 문자열(JWT 토큰) 응답을 받을 거야"

2. Spring (자동 구현)

  • 스프링은 위 인터페이스를 보고 실제 HTTP 요청을 보내는 코드를 자동으로 만듬

3. 컨트롤러에서 사용하기


@Controller
public class AuthController {
    // Spring이 자동으로 만든 구현체를 여기에 넣어줌
    private final AuthAdaptor authAdaptor;
    
    @PostMapping("/api/auth/login")
    public String login(...) {
        // 사용자가 입력한 이메일과 비밀번호를 Map에 담기
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("email", email);
        requestBody.put("password", password);
        
        // 백엔드 API 호출하기 !!🌟
        ResponseEntity<String> responseEntity = authAdaptor.login(requestBody);
        
        // 응답 처리하기
        // ...
    }
}

4. 실제 HTTP 요청

POST /api/auth/login HTTP/1.1
Host: api.blog.url
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "userPassword123"
}

5. 백엔드 API 응답

HTTP/1.1 200 OK
Content-Type: text/plain

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

1.인증 관련 Adaptor 생성

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.Map;

@FeignClient(name = "authAdaptor", url = "${api.blog.url", path = "/api/auth")
public interface AuthAdaptor {
	// "/api/auth/login" 경로로 POST 요청을 보내는 메서드 선언
    @PostMapping("/login")
    ResponseEntity<String> login(@RequestBody Map<String, String> loginRequest);
}
  • Feign Client 선언: 백엔드 API와 통신하기 위한 인터페이스

  • name은 스프링에서 빈으로 등록할 때 사용

  • url : 요청을 보낼 서버의 기본 url(application.properties)

  • path : API 기본 경로

  • 요청 본문에서 Map형태로 이메일 비밀번호를 담아서 전송

  • 응답으로 JWT 토큰을 받음

2. 인증 컨트롤러 생성


@Controller
@RequiredArgsConstructor
public class AuthController {
    private final AuthAdaptor authAdaptor;
    
     @PostMapping("/api/auth/login")
    public String login(@RequestParam String email, @RequestParam String password,
                        HttpServletResponse response, RedirectAttributes redirectAttributes){
	}
}
  • 웹 요청을 처리하는 컨트롤러임을 어노테이션으로 명시
  • 백엔드 API와 통신하기 위해 AuthAdaptor 주입 받음
  • 폼에서 전송된 email, password 파라미터를 받음
  • 또한 파라미터로 리다이렉트 시 데이터 전달을 위한 객체를 받아줌
// 응답 본문에서 JWT 토큰을 추출
String token = responseEntity.getBody();
  • 만약 백엔드에서 응답이
HTTP/1.1 200 OK
Content-Type: text/plain

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW1wbGUuY29tIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

이렇게 온다면 getBody는

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW1wbGUuY29tIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

반환 하는데 이것이 JWT 토큰

2-1. Login 메소드 구현

메소드가 해야하는 역할
1. /api/auth/login 경로로 POST 요청이 도착
2. @RequestParam으로 이메일과 비밀번호를 받음
3. 이메일과 비밀번호를 Map에 담아 JSON 형태로 변환

  // 백엔드 API에 보낼 JSON 데이터를 준비 (이메일과 비밀번호)
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("email", email);
            requestBody.put("password", password);
  1. AuthAdaptor를 통해 백엔드 API 호출
		ResponseEntity<String> responseEntity = authAdaptor.login(requestBody);
  1. 백엔드 API에서 인증 처리 (이메일, 비밀번호 검증 후 성공시 JWT 토큰 생성 후 응답)

  2. 응답 코드가 200인지, 응답 본문에서 JWT 토큰 추출

if(responseEntity.getStatusCode().is2xxSuccessful(){
	// 응답 본문에서 토큰 추출
	String token = responseEntity.getBody();
    
    // jwtToken 이라는 이름의 쿠키 생성
    Cookie cookie = new Cookie("jwtToken", token);
    
    // 쿠키 경로 "/"로 설정(모든 페이지에서 사용이 가능하게)
    cookie.setPath("/");
    
    // 자바스크립트에서 쿠키 접근 할 수 없게 설정(XSS 공격방지)
    cookie.setHttpOnly(true);
    
    // 7. 쿠키를 HTTP 응답에 추가
    response.addCookie(cookie);
}
  1. 성공시 홈페이지로 리다이렉트, 실패시 오류메세지와 함께 로그인 페이지로 이동
                // 홈페이지로 리다이렉트
                return "redirect:/";
            }else{
                 // 로그인 실패시 로그인 페이지로 리다이렉트
                redirectAttributes.addAttribute("error", "true");
                return "redirect:/login";
            }
        }catch(Exception e){
            redirectAttributes.addAttribute("error", "true");
            return "redirect:/login";
        }

2-2. Logout 메소드

    @GetMapping("/logout")
    public String logout(HttpServletResponse response){
        //JWT 토큰 쿠키 삭제
        Cookie cookie = new Cookie("jwtToken", null);
        cookie.setPath("/");
        cookie.setMaxAge(0);
        response.addCookie(cookie);

        return "redirect:/login?logout=true";
    }
  • 쿠키값 null 설정
  • 모든 경로에서 삭제되도록 설정 후
  • 쿠키 즉시 만료 : setMaxAge(0)
  • 응답에 쿠키 추가 후
  • 로그아웃 메세지와 함께 로그인 페이지로 이동

현재까지 구현된 사항

게이트웨이에서 JWT 인증 필터 구현

  • JwtProvider 클래스: 토큰 검증 및 사용자 정보 추출
  • JwtAuthenticationFilter 클래스: 요청 필터링 및 인증 처리

프론트엔드에서 인증 관련 기능 구현

  • AuthAdaptor 인터페이스: 백엔드 API와의 통신
  • AuthController 클래스: 로그인 및 로그아웃 처리
  • 쿠키 기반 JWT 토큰 저장 및 관리

0개의 댓글