Spring security 는 기본적으로 (어떤 방식으로든) 인증이 된 사용자는 SecurityContext에 저장을 해둡니다. 이는 기본 설정상으로 threadlocal 이라 전역변수처럼 사용할 수 있습니다. (controller / service 어디서든 현재 이 요청을 보내는 사용자에 대한 정보를 받을 수 있다는 뜻)
401 Response는 인증 오류, 403은 인증은 완료되었는데 권한이 없는 오류입니다.
권한 설정은 동적으로 하는게 편해보여서 기본적으로 모든 요청은 인증이 없어도 되도록 허락하고(securityConfig -> permitAll), 인증/인가가 필요한 요청은 어노테이션으로 지정할 수 있습니다(PartyRoomController)
Authentication (인증)
로그인 시에는 DB에 저장된 사용자 정보와 사용자가 입력한 정보를 비교해서 일치하지 않으면 예외를 던집니다. -> 401 resposne
로그인이 성공하면 사용자 정보(이메일 / 권한)과 유효시간을 종합해서 토큰화해서 client에 돌려줍니다. (이후 클라이언트는 이 토큰을 헤더에 넣어서 요청을 보냅니다)
로그인 이외의 인증은 토큰으로 이루어집니다. JwtFilter가 이를 담당하는데 JwtFilter는 유효한 JWT 토큰에 한해 이를 파싱해서 사용자 정보를 security context에 저장합니다.
유효한 토큰이 아니거나 만료시간이 경과했다거나 등의 이유로 로그인이 다시 필요하다면 401 에러를 반환합니다.
Authorization (인가)
권한 확인은 동적으로 가능한게 편한 것 같아서 이 방법을 사용했습니다.
@PreAuthorize( "hasRole('ROLE_HOST')" ) 어노테이션을 사용하면 HOST 권한이 있는 사람만 해당 메서드에 접근할 수 있습니다.
만약 위의 어노테이션을 가진 메서드에 접근하려고 하는데
로그인이 되어있지 않거나 유효기간이 지났다면 401 에러를 반환합니다
로그인은 되어있는데 권한이 없다면 403 에러를 반환합니다.
spring security 가 어떻게 알고 에러를 구분하는지 좀 파봤는데.. 답은 security context 에 있었습니다.
어쨋든 우리는! 저 어노테이션만 잘 쓰면 됩니다.
경우에 따라서 로그인한 유저가 남의 정보를 수정하려고 할 때가 있습니다. 위 어노테이션은 HOST 권한만 파악하기 때문에 param body를 조작하면 남의 정보도 수정할 수 있는 것이죠. 따라서 이때에는 토큰 기반의 loginUser 와 변경 대상의 주인(dto의 주인) 을 비교해서 동일한 경우에만 허락해야 합니다.
적용
크게 네 가지 영역으로 나눌 수 있습니다.
누구나 접근할 수 있는 요청( e.g. 회원가입 / 로그인 / 파티룸 조회)
로그인이 필요한 요청 (e.g. 파티룸 예약하기)
로그인 + 권한이 필요한 요청 (e.g. 파티룸 등록하기는 호스트 권한을 가진 사용자만 가능)
로그인 + 권한 + 주인체크(를 비롯한 비즈니스 체크)가 필요한 요청 (e.g. 파티룸 수정하기는 호스트 권한을 가진 사용자 + 자기 자신의 파티룸만 수정 가능 / 리뷰를 남기는 유저는 유저 권한을 가진 사용자 + 예약내역이 있는 경우에만 남기기 가능 등)
각 영역에 대한 접근법입니다.
1번 영역은 별도의 설정 없이 컨트롤러를 만들면 됩니다. 플젝의 기본 세팅이 별도의 설정이 없으면 permitAll로 되어있습니다.
2번 영역은 컨트롤러(심지어 서비스에서도 가능!!)의 메서드에 @PreAuthroized("isAuthenticated()") 를 적용하면 로그인한 유저만 접근 가능합니다. 로그인되지 않은 유저는 401에러를 받게 됩니다.
3번 영역은 위에서 설명한 @PreAuthorize( "hasRole('ROLE_HOST')" 와 같은 어노테이션을 사용하면 됩니다. HOST 부분은 필요에 따라 USER / HOST / ADMIN (혹은 미래에 생길 권한까지) 로 변경될 수 있습니다.
로그인 자체가 안된 유저는 401 (인증오류니까) / 로그인은 되어있으나 권한이 없는 유저는 403 (인가오류니까) 를 받게됩니다.
4번 영역에 대해서는 PartyRoomController에 예시를 들어놨습니다. 3번처럼 @PreAuthorize 까지는 해줘야합니다. 그러면 아예 로그인이 안된 유저 / 권한이 없는 유저는 오류를 받게됩니다. 이에 추가적으로 로그인한 유저와 유저가 변경하려고 하는 대상의 정보와 일치 여부를 판단해야 합니다. 만약 다를 경우 인가오류와 마찬가지로 403 AccessDenied 에러를 받게 됩니다.
Diagram - Login flow
User with correct email/password gets token response
User with incorrect email/password gets 401 Unauthorized
Diagram - Authorization Flow
Users that have authentication(= is logged in) and have correct roles will get access to business logic
Uses that are not logged in will get 401 Error
Users that are logged in, but do not have proper roles will get 403 Error
Basic
Authentication (인증)
Authorization (인가)
적용
크게 네 가지 영역으로 나눌 수 있습니다.
각 영역에 대한 접근법입니다.
Diagram - Login flow
Diagram - Authorization Flow
Lucid chart url: https://lucid.app/lucidchart/d5a5cd20-285c-4447-9d0e-caa2fb51b1a7/edit?page=SweiZA9NLL0U&invitationId=inv_d98c8207-966b-4ed4-8694-3c3b44b83ce0#