Authentication의 이해
이전 포스팅에서는 스프링 시큐리티의 간단한 소개와 Session으로 인증받는 형태와 시큐리티의 차이점에 대해서 이야기해보았다. SecuriyContext에 대해서 이야기하다가 끝냈는데 여기서는 Authentication에 대해서 한번 알아보자.
SecurityContext는 앞에서도 이야기했지만 말 그대로 인증 정보를 담는 객체일 뿐이었다. 그리고 Authentication은 영어 그대로 인증 정보를 의미하는 인터페이스이다.
실제 스프링 시큐리티에서는 기본적으로 아이디와 패스워드로 인증할 때에는 이 인터페이스를 상속받은 UsernamePasswordAuthenticationToken 클래스를 기본으로 해서 인증을 받고 있다.
( 이 토큰에 대해서도 간단히 설명하겠다. )
먼저 Authentication의 내부 구조를 간단하게 살펴보자.
Object getPrincipal();
Object getCredentials();
Object getDetails();
Collection <? extends GrantedAuthority> getAuthorities();
//이하 생략
일단 주요 메서드만 살펴보겠다.
Object getPrincipal()
처음 이름을 보면 혼란이 오는 사람들도 있지만 간단하게 기존 형태(내부적으로 설계가 되어 있지 않고 직접 간단히 인증시스템 구현되어 있을 때 기준으로)와 비교해서 한번 살펴보겠다.
첫 번째 포스팅에 기술한 예제 기준으로 보면 사용자 인증 여부(즉 로그인 여부)를 파악할 때에는 HttpSession에 인증정보가 있는지 여부를 확인한다.
Object sessionMember = session.getAttribute("member");
if (sessionMember == null) {
// 로그인 안되었음
} else {
// 로그인되었음 ( 인증되었음 )
}
즉 세션에 저장된 사용자 정보의 존재 여부가 중요하고 이 정보 기준으로 사용자를 구분한다.
(활동의) 주도자, 주역; 주연 배우; 주 연주자.
[법률] (대리인에 대한) 본인; (보증 채무의) 주채무자; 정범, 주범
여러 가지 뜻이 있지만 가장 여기에서 가장 적절하게 생각되는 뜻이다.
여기서 반환하는 부분이 Object인데 어떠한 객체라도 들어갈 수 있다는 뜻이다. 즉 시큐리티 프레임워크에서 인증을 하면 인증한 객체 (DB에서 가져온 사용자 정보)를 principal이라는 메서드로 읽어 올 수 있는 것이다.
UsernamePasswordAuthenticationToken 같은 경우에는 인증받을 때에는 아이디가 전달되며 인증받은 후에 DB에서 받은 객체(예제에서는 Member 객체)로 변경된다.
(여기서 많이들 헷갈리는 부분이다.)
Object getCredentials()
자격
자격 인증서, 자격증
이름 그대로 자격이다. Principal에서 사용자 본인을 의미한다면 Credentials은 그에 따른 자격을 증명하고자 할 때 사용된다. Principal과 마찬가지로 구현에 따라 어떠한 정보가 들어갈지 달라지지만 대체적으로 암호화된 정보를 저장하며, 보안에 신경을 많이 써야 되는 정보이다.
UsernamePasswordAuthenticationToken 같은 경우에는 사용자의 패스워드가 들어간다.
인증이 완료 후에는 기본적으로 제거하고 있다. (옵션에서 변경 가능)
Object getDetails()
이 부분은 현재 인증 중이거나 인증된 사용자의 부가적인 정보를 저장한다.
예를 들면 인증될 때 전달된 HTTP UserAgent 정보를 파싱 해서 넣어두는 형태로 어떻게 로그인했는지 정보를 저장하거나 특정 파라미터를 받거나 하는 형태 대도 생각해볼 수 있다.
Principal과 다른 점은 직접적으로 사용자를 나타내는 정보가 아니라 인증 시에 부가적인 정보를 저장하는데 많이 사용된다. ( 예를 들면 어디에서 인증받았는지 어떻게 인증받았는지 리모트일 때는 IP가 뭔지 등등 )
UsernamePasswordAuthenticationToken 같은 경우에는 기본적으로 사용자의 인증 시에 전달된 HttpServletRequest의 정보에서 필요한 정보(아이피, 세션 아이디)를 가져와서 저장된다. (필요하면 커스터마이징이 가능하다.)
Collection <? extends GrantedAuthority> getAuthorities()
이 메서드는 시큐리티 자체에서 중요한 역할을 하고 있다.
먼저 첫 번째 포스팅에서 이야기한 소스 부분을 살펴보자.
boolean isAuthority = false;
if (uri.startsWith("/member/")) {
if (member.hasRole("ROLE_USER") ||
member.hasRole("ROLE_ADMIN")) {
isAuthority = true;
}
}
if (uri.startsWith("/dev/")) {
if (member.hasRole("ROLE_USER") ||
member.hasRole("ROLE_TEST"))
isAuthority = true;
}
if (isAuthority) {
chain.doFilter(req, res);
}
필터에서 사용자의 권한 별로 어디에 URL 에 접속할 수 있을지 검사하는 로직이다.
사실 이런 식으로 하드코딩 형태로 해두진 않는다. 보통 DB에 저장해두며 특정 URI가 어떤 권한 정보만 접속 가능하다는 형태로 저장해두면 사용자는 해당 ROLE정보만 가지고 있으면 된다.
즉 이 메서드는 현재 사용자의 ROLE 정보를 가져오는 부분이다.
GrantedAuthority 클래스는 사용자의 지정한 권한 범위를 기술하기 위해 추상화된 클래스이다.
(권한이 ROLE만 있는 게 아니다. 여러 가지 조건으로 제한이 가능하다.)
UsernamePasswordAuthenticationToken 같은 경우에는 기본적으로 인증 시에는 emptyList를 리턴하며 인증 완료 후에 사용자의 권한 정보(ROLE 및 지정된 권한식 등)가 저장된다.
그리고 사용자의 권한을 검사할 때 이 메서드 기준으로 검사하게 된다.
이 인터페이스는 시큐리티에서 시스템에서 접근 시 인증정보를 추상화시킨 인터페이스이다.
시큐리티에서는 주로 UsernamePasswordAuthenticationToken이 사용되며 인증 시, 인증 후 값이 달라지는 부분에 주목해야 한다.
Authentication 인터페이스의 메서드에 대해서 이야기해보았다.
인터페이스 하나로 일일이 하나씩 기술해서 포스팅을 하였는데 그 이유는 스프링 시큐리티 프레임워크의 핵심 이기 때문이다.
사실 소스도 별로 없이 이야기했기 때문에 무슨 이야기 하고 있는지 이해 안 가시는 분들도 있을 것이 이라 생각된다. 다음 포스팅에는 소스를 통해서 실제로 현재 스프링 시큐리티의 기초 포스팅을 시작하면서 실제로 이야기하고 싶은 부분에 대해서 다루려고 한다.