인프런의 "Spring Cloud로 개발하는 마이크로서비스" 강의를 보고 작성되었습니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4
이번 강의에는 로그인 기능을 구현하여 로그인하고, 로그인이 완료되면 response 헤더에 json 웹 토큰을 전달합니다. 그리고 이 토큰을 다른 서비스에서 API 호출 시 인증 여부를 확인하는 필터를 구현합니다.
로그인 시 입력받는 이메일과 비밀번호를 저장합니다.
Security로 로그인 요청 시 작업을 처리하는 필터로, UsernamePasswordAuthenticationFilter를 상속받아 attemptAuthentication 메소드를 구현합니다.
로그인 api는 POST 형태로 request 파라미터로 받을 수 없어 request.getInputStream()로 로그인 시 입력한 이메일과 비밀번호로 RequestLogin으로 변환합니다.
UsernamePasswordAuthenticationtoken 으로 변경하여 AuthenticationManager에 인증 작업 처리를 요청합니다.
이 부분에서 사용자 요청에 대해 AuthenticationFilter를 거치도록 추가해줍니다.
UserDetailsService 의 loadUserByUsername을 통해 사용자가 입력한 name (프로젝트에서는 이메일), 비밀번호로 로그인 시 인증을 처리합니다.
이때, DB에 저장된 EncryptedPassword와 로그인 시 입력한 비밀번호를 encode 하여 비교합니다.
jwt에 사용할 토큰의 유효 시간 등의 정보를 application.yml에 추가하고 Environment 혹은 @Value로 사용합니다.
Security 설정에서 userDetailsService를 등록하기 위해 UserDetailsService 인터페이스를 구현합니다.
UserServiceImpl에서 loadUserByUsername() 메소드를 구현합니다.
findByEmail을 통해 DB에서 저장된 데이터를 가져와 Spring Security의 User 객체를 생성합니다. User객체는 입력 받은 사용자의 정보와 비교하는 데 사용됩니다.
지금까지는 /user-service/** 의 Path로 api를 호출하면 user-service 에서 모든 Path를 포함하여 uri가 매핑되었는데, filters의 RewritePath를 통해 user-service를 지우고 뒤의 패턴만 전달될 수 있도록 변경 했습니다.
다음과 같이 RequestMapping 의 uri에서 /user-service가 삭제 가능합니다.
8000번 포트의 gateway를 사용해서 회원가입과 로그인이 잘 동작하는 모습입니다.
사용자가 로그인 요청 - 아이디와 비밀번호 입력
AuthenticationFilter에서 처리 - UsernamePasswordAuthenticationToken 형태로 변경
UserDetailsService를 구현한 클래스에서 loadUserByUsername 메소드 실행
Repository에서 DB에 접근해 entity를 가져와 Security의 User 객체로 변경
인증이 성공적으로 완료되면 AuthenticationFilter의 successfulAuthentication 메소드 실행 - 이번 프로젝트에서는 이메일로 다시 저장된 유저의 userId 값을 가져와 JWT 생성
생성된 토큰을 response header 에 저장하여 반환
AuthenticationFilter에서 인증 요청 완료 시 실행되는 메소드를 구현했습니다.
이때, DB에서 저장된 userId 값을 가져와 토큰에 담아 JWT 토큰을 생성합니다.
생성된 토큰과 userId을 reposnse 헤더에 담아 반환해 줍니다.
Postman을 통해 확인하면 응답 헤더에 token 과 userId 값을 확인할 수 있습니다.
인증 헤더 Authorization 에 사용
기존 필터와 같이 AbstractGatewayFilter를 상속받아 apply 메소드 를 구현했습니다.
사용자가 요청 시 헤더에 토큰을 담아서 전달하게 되면, Authorization 헤더에서 Bearer {token} 값을 받아 인증을 처리합니다.
isJwtValid 메소드를 작성해 토큰 값 유효성 검사를 진행했습니다.
로그인과 회원 가입 시에는 토큰 인증을 거치지 않아도 되기 때문에 제외하고 나머지 user-service 에서 호출하는 api는 AuthorizationHeaderFilter를 거쳐 인증이 필요하도록 추가했습니다.
로그인, 회원 가입이 아닌 api 요청 시 토큰 값을 넣지 않으면 401 에러가 발생합니다.
헤더에 다음과 같이 토큰 값을 넣고 api 요청을 하면 정상적으로 동작하는 모습을 볼 수 있습니다.