1년 전 진행했던 마실가실 프로젝트를 🛠️리팩토링하며 정리한 내용입니다.
JWT 사용 방식에 대해 알고 계신가요..?
공식 문서에서는 Authorization Header에 Bearer를 붙여 토큰을 발해서 보내라, 라고 하고 있습니다.
문득, 제 지난 코드는 Header가 아닌 Body를 쓰고 있다는 사실을 알게 되었고, 그로 인해 토큰 재발급과 로그아웃을 리리팩토링하게 되었습니다.
공식 문서가 시키는대로 완성된 코드를 정리해 보고자 합니다.
1. 로그인
LoginFilter.java
Spring Security에서 제공하는 폼을 사용하면 UsernamePasswordAuthenticationFilter를 통해서 로그인 인증이 진행됩니다.
저는 별도의 회원가입 양식을 사용하기 때문에 UsernamePasswordAuthenticationFilter를 상속받아 커스텀한 LoginFilter를 사용하였습니다.
attemptAuthentication 함수를 통해 인증을 수행하게 되고, 이를 통과할 경우에는 successfulAuthentication 함수가, 도중에 오류가 발생할 경우, unsuccessfulAuthentication 함수가 동작하게 됩니다.
로그인 성공 후, successfulAuthentication 함수에서는
Access token과 Refresh token을 발급해서 Access Token은 Authroization Header에 저장하고 Refresh Token은 쿠키를 사용해서 사용자에게 전달합니다.
* Access token: JavaScript에서 access token을 직접 Authorization 헤더에 넣어 전송해야 하므로, CSRF 공격을 방지
* CSRF 공격: 공격자가 사용자를 속여서 원하지 않는 요청을 보내게 만드는 공격(사용자 권한 관련 피해)
* Refresh token: HttpOnly를 설정하여 JavaScript 접근을 막아 XSS 공격을 통해 탈취되는 것을 방지
* XSS: 웹사이트에 악성 스크립트를 삽입하여 다른 사용자가 해당 스크립트를 실행하게 만드는 공격(개인 정보 탈취 피해)
2. 토큰값 검증
JWTFilter.java
Authorization Heaer를 찾아서 Token을 반환하고, 이 토큰의 유효성 검증을 위한 필터를 추가합니다.
SecurityContextHolder에 token을 통해 발급한 Authentification을 저장해둡니다.
3. 토큰 재발급
UserService.java
Refresh token은 Access token을 재발급하기 위해 사용하는 token으로 재발급을 위해 필터까지 설정하지 않습니다.
일반적으로 특정 API 엔드포인트에서 처리되기 때문에 저 또한 Controller에서 받아 Service에서 처리하였습니다.
Refresh token을 통해 재발급받을 경우, Refresh token까지 재발급하도록 작성하였습니다.
4. 로그아웃
CustomLogoutFilter.java
토큰의 유효성 검사 및 Redis안에 Token 존재여부를 판단하기 위해 기존 LogoutFilter 전에 커스텀한 Logout 필터를 추가했습니다.
🚨 쿠키 설정 시, 유의 사항은 쿠키 생성과 동일한 조건으로 진행해야 쿠키가 제대로 삭제됩니다.
5. SecurityConfig 설정
SecurityConfig.java
설정한 필터들을 순서에 맞게 등록합니다.
* 토큰 검증을 하는 JWTFilter는 LoginFilter 전에
* 로그인 인증을 검사하는 UsernamePasswordAuthenticationFilter 대신 LoginFilter를
* 로그아웃을 수행하는 LogoutFilter 전에 CustomLogoutFilter를 추가해줍니다.
☺️그럼 이제는 Header와 Cookie를 통해서 Access token과 Refresh token을 전달할 수 있게됩니다!
🙋♀️
본 포스트는 공부 목적으로 작성하였습니다.
보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.
📑
참고 자료
https://www.youtube.com/playlist?list=PLJkjrxxiBSFATow4HY2qr5wLvXM6Rg-BM
'PlayGround > 마실가실 리팩토링' 카테고리의 다른 글
[1년 후 마실가실] @GeneratedValue(strategy = GenerationType.IDENTITY) (0) | 2024.11.14 |
---|---|
[1년 후 마실가실] CharacterEncodingFilter (4) | 2024.11.10 |
[1년 후 마실가실] Spring Security + JWT - LoginFilter (0) | 2024.10.27 |
[1년 후 마실가실] User Error Code HttpStatus (1) | 2024.10.19 |
[1년 후 마실가실] Controller Test Code - mockMvc.perform() (0) | 2024.10.17 |