Skip to content

인가 과정

Donghoon Shin edited this page Nov 21, 2022 · 1 revision

인가 과정 문서화

요약

  • AuthMemberPrincipal 은 멤버의 권한 설정 (Interceptor)
    • AccessToken 이 Null or Empty 면 ANOYMOUS (비회원 사용자)
    • 토큰이 있고, 유효한 토큰이면 MEMBER
    • 토큰이 있는데, 유효하지 않은 토큰이면 TokenNotValidExcetpion 발생
  • MemberOnly 는 멤버만 접근할 수 있도록 함 (AOP)
    • 권한을 확인해야 하기 때문에 해당 요청의 파라미터에 @AuthMemberPrincipal 필수 등록
    • 권한이 MEMBER 가 아니면 MemberNotAllowedException 발생

사용법 예시

@PostMapping
@MemberOnly
public ResponseEntity<Void> create(@AuthMemberPrincipal LoginMember member,
                                   @RequestBody LevelLogRequest levelLogRequest) {
		final LevelLogResponse response = levelLogService.insertLevellogs(member.getId(), levelLogRequest);
		return ResponseEntity.created(URI.create("/levellogs/" + response.getId())).build();
}

동작 과정

AuthMemberPrincipal

@AllArgsConstructor
public class AuthMemberPrincipalArgumentResolver implements HandlerMethodArgumentResolver {

    private final JwtTokenProvider jwtTokenProvider;
    private final MemberAuthorityCache memberAuthorityCache;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(AuthMemberPrincipal.class);
    }

    @Override
    public LoginMember resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                       NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
				// 1
        String credentials = AuthorizationExtractor
            .extract(webRequest.getNativeRequest(HttpServletRequest.class));

				// 2
        if (credentials == null || credentials.isEmpty()) {
            memberAuthorityCache.setAuthority(Authority.ANONYMOUS);
            return new LoginMember(Authority.ANONYMOUS);
        }

				// 3
        try {
            Long id = Long.parseLong(jwtTokenProvider.extractSubject(credentials));
            memberAuthorityCache.setAuthority(Authority.MEMBER);
            return new LoginMember(id, Authority.MEMBER);
        } catch (NumberFormatException e) {
            throw new TokenNotValidException();
        }

    }
}
  1. HttpServletRequest 의 Authorization 헤더 필드에서 토큰을 추출합니다.
    • Authrization 헤더 필드에 토큰이 존재한다면 credentials 변수에 할당됩니다.
    • 토큰이 존재하지 않는다면 null 이 할당 됩니다.
  2. 비회원 사용자를 걸러냅니다.
    • credentials 가 null 혹은 Empty 면 비회원 사용자입니다.
    • memberAuthorityCache의 Authority 가 ANONYMOUS 로 설정됩니다.
    • LoginMember 객체를 ANONYMOUS 권한으로 생성해 반환합니다.
  3. 회원인 사용자에 대한 검증입니다.
    • 토큰을 검증하면서 회원의 id 값을 받습니다.
      • 이 때, 토큰이 유효하지 않다면 예외가 발생합니다.
    • memberAuthorityCache의 권한을 MEMBER로 설정합니다.
    • LoginMember 객체를 MEMBER 권한으로 생성해 반환합니다.

MemberAuthorityCache

@Component
@RequestScope
public class MemberAuthorityCache {

    private Authority authority;

    public Authority getAuthority() {
        return authority;
    }

    public void setAuthority(Authority authority) {
        this.authority = authority;
    }
}
  • 해당 요청을 한 멤버의 권한을 잠시 저장해놓는 객체입니다. (Spring Bean)
  • @Reqeust 가 선언되어 있는데, 이는 스프링 Bean 의 생명주기가를 현재 들어온 요청으로 제한하는 것입니다.(디폴트는 싱글톤)
  • AuthMemberPrincipalArgumentResolver 를 통해 MemberAuthorityCache 의 Authority를 설정하고, MemberOnly AOP 과정에서 이를 이용해 권한을 검증합니다.

MemberOnly

@Aspect
@Component
@RequiredArgsConstructor
public class LoginMemberVerifier {

    private final MemberAuthorityCache memberAuthorityCache;

		//1
    @Before("@annotation(wooteco.prolog.login.aop.MemberOnly)")
    public void checkLoginMember() {
				//2
        final Authority authority = memberAuthorityCache.getAuthority();
        if (!authority.equals(Authority.MEMBER)) {
            throw new MemberNotAllowedException();
        }
    }
}
  • Spring AOP를 통해 멤버 권한 체크를 진행합니다.
  1. MemberOnly 어노테이션이 존재하는 메서드의 실행전에 checkLoginMember 메서드를 실행합니다.
  2. AuthMemberPrincipalArgumentResolver 를 통해 설정해둔 MemberAuthorityCache 의 Authority 로 권한 체크를 합니다.
    • MEMBER 권한이 아니면 예외를 발생합니다.
Clone this wiki locally