3주차 미션을 진행하면서 인증관련 내용이 추가가 되었는데 처음 접하는 내용이라 많이 어려웠다. 스프링 시큐리티를 이용하게 되면 미션 난이도가 많이 올라가게 되어서 비록 스프링 시큐리티를 온전히 사용하지는 않았지만 그래도 어느정도 관련성은 있으므로 정리를 해두려고 한다.
프론트 단에서 이메일과 패스워드를 입력하여 로그인 요청을 보내면 서버에서는 이 로그인 요청을 /login/token의 경로로 보낸다.
AuthConfig는 인증과 관련된 설정을 하는 곳으로 위 사진의 빨간색 부분에 의하여 /login/token으로 요청이 오면 TokenAuthenticationInterceptor를 실행시킨다.
로그인 요청에는 이메일과 비밀번호에 대한 정보가 들어있는데 JSON형태로 들어있다.
따라서 JSON 형태의 정보를 JAVA에 맞게 다시 재가공해야하는데 이 역할을 하는 메소드가 바로 convert이다.
위 사진의 빨간색 부분이 JSON 형식의 정보를 JAVA에 맞게 재가공해주는 코드이며,
TokenRequest
객체와 AuthenticationToken
객체는 다음과 같다.
public class TokenRequest {
private String email;
private String password;
public TokenRequest(String email, String password) {
this.email = email;
this.password = password;
}
}
public class AuthenticationToken {
private String principal;
private String credentials;
public AuthenticationToken(String principal, String credentials) {
this.principal = principal;
this.credentials = credentials;
}
}
principal은 로그인 요청 정보의 EMAIL이다.
빨간색 부분을 보면 DB에서 해당 EMAIL을 가진 회원 정보를 가져온 뒤 로그인 요청 정보의 패스워드와 회원 정보의 패스워드가 일치하는지 검증하는 코드이다.
만약 검증을 통과하였다면 DB에서 가져온 회원 정보를 Authentication
객체에 담는다.
Authentication
객체는 다음과 같다.
public class Authentication {
private Object principal;
public Authentication(Object principal) {
this.principal = principal;
}
}
정리하면 DB에서 가져온 회원 정보를 Authentication
의 principal
이 가지고 있는 것이다.
파란색 부분을 집중해서 보길 바란다.
authentication.getPrincipal()
에는 회원정보가 들어있는데 이를 payload
라는 변수에 담는다.
그리고 JWT 라이브러리를 이용해 이 payload
로 토큰을 만든다.
그 다음 tokenResponse
객체에 그 토큰을 담는다.
주황색 부분은 응답 Response를 보내는 부분이다.
주황색 코드로 인하여 응답으로 다음과 같은 JSON이 나가게 된다.
{accessToken : 해당 토큰 값}
여기까지가 로그인 요청을 보내고 로그인이 되는 과정이다.
로그인이 성공적으로 되면 accessToken이라는 토큰 값을 응답 받게 된다.
또한 여기까지의 과정을 인증(Authentication)의 과정이라 부른다.
즉 유저가 누구인지 확인하는 과정이라고 생각하면 되겠다.
로그인에 성공하였다면 accessToken
을 받았을 것이다.
내 회원 정보 조회 요청을 하려면 당연히 로그인이 되어있어야 한다.
따라서 내 회원 정보 조회 요청에 대한 url인 /members/me로 요청을 보내기 전에 위 사진의 빨간색 부분의 과정이 필요하다.
즉 내 회원 정보 조회 요청을 보낸 클라이언트가 응답을 받을 권한이 있는지 판단을 할 수 있는 인증 정보(토큰)를 추가해주는 것이다. 이 부분이 빨간색 부분에 해당한다.
더 자세히 말하자면 빨간색 부분이 추가된다면 요청의 헤더에 Authorization: Bearer (accessToken값)
이 추가가 되고 헤더에 이 값이 있으면 서버에서는 권한을 판단하는 과정을 처리하게 되는 것이다.
또한 이 과정을 인가(Authorization)라고 부른다.
즉 요청을 실행할 수 있는 권한 여부를 확인하는 과정이라고 생각하면 되겠다.
AuthConfig는 인가와 관련된 설정 또한 하는 곳으로 위 사진의 빨간색 부분에 의하여
어떤 요청에 대해서 토큰과 관련된 권한 여부를 확인할 때 TokenSecurityContextPersistenceInterceptor가 실행된다.
이 코드에 대한 분석을 하기 전에 미리 알아둬야 할 것에 대해 정리를 하겠다.
SecurityContextHolder는 SecurityContext를 담고 있는 저장 공간이라고 생각하면 된다.
SecurityContext에는 Authentication을 필드로 가지고 있는데 여기에 회원의 정보를 담는다.
파란색 부분부터 설명을 하고자 한다.
파란색 부분은 accessToken
이 유효한 토큰인지 검증하는 과정이다.
만약 validateToken
에서 false가 나온다면 즉 이미 유효기간이 지난 토큰이라면 SecurityContext
에 회원정보를 담지 않고 그냥 통과시키게 한다.
주황색 부분은 만약 accessToken
이 유효한 토큰이라면 회원 정보를 담고 있는 accessToken
에서 회원 정보를 추출하여 SecurityContext
의 authentication
에 회원정보를 담는 코드이다.
빨간색 부분은 만약 SecurityContext
에 이미 회원정보가 담겨있다면 파란색부분과 주황색 부분은 필요가 없으므로 그냥 통과시키는 코드이다.
여기까지가 요청이 들어왔을 때 권한이 있는지 없는지 판단하는 과정이다.
다음은 권한이 있다고 판단이 되었을 때 요청을 수행하는 과정이다.
이 어노테이션은 회원 정보에 대한 객체 즉 LoginMember
객체를 얻어올 때 쉽게 도와주는 어노테이션인데 일단 이게 있다는 것만 알아두고 넘어가자 (밑에 설명이 또 나온다!)
주황색 부분은 각 컨트롤러 메소드의 인자에 @AuthenticationPrincipal
가 붙어있는지 안붙어있는지 판단하는 코드이다.
빨간색 부분이 중요한데 만약 컨트롤러 메소드의 인자에 @AuthenticationPrincipal
가 붙어있다면 회원 정보가 저장되어 있는 SecurityContext
의 Authentication
을 반환하는 코드이다.
이 Resolever
덕분에 @AuthenticationPrincipal
를 이용하면 회원 정보에 대한 객체 즉 LoginMember
객체를 쉽게 얻어올 수 있게 되는 것이다.
이제 내 회원 정보 조회에 대한 요청에 대한 응답을 내보내어야 한다.
컨트롤러 메소드 findMemberOfMine
의 인자에 @AuthenticationPrincipal
가 붙어 있으므로 SecurityContext
에 들어있는 회원정보를 LoginMember
객체가 받게 된다.
따라서 이 LoginMember
객체의 정보를 응답값으로 보내주면 된다.