프로젝트에서 Spring Security + JWT + Redis 방식으로 인증/인가 로직을 구현하였고, 전체 과정을 기록으로 남겨두려 한다.
⇒ 즉, 먼저 사용자가 맞는지 인증한 후, 인증된 사용자에 한해 인가 절차를 수행한다.
Ex) 로그인을 통해 인증되면 url 별로 설정된 접근 권한에 따라 인가 여부가 결정된다.
hasRole()
과 같은 메서드로 설정할 수 있는데, 해당 정보가 SecurityMetadataSource에 의해 반환된다.MODE_THREADLOCAL
, MODE_INHERITABLETHREADLOCAL
, MODE_GLOBAL
방식을 제공한다.MODE_??
MODE_THREADLOCAL
MODE_INHERITABLETHREADLOCAL
MODE_GLOBAL
try {
Authentication authentication = Objects.requireNonNull(SecurityContextHolder
.getContext()
.getAuthentication());
if (authentication instanceof AnonymousAuthenticationToken) {
authentication = null;
}
return authentication.getName();
} catch (NullPointerException e) {
throw new RuntimeException();
}
AbstractAuthenticationToken
추상 클래스를 상속받아 구현된다.AbstractAuthenticationToken
authenticated
변수를 갖고 있으며, setAuthenticated() 메서드를 통해 인증되면 true, 아니면 false가 저장된다.authenticate()
메서드가 있는 Interface이다.authenticate()
Authentication authenticate(Authentication var1) throws AuthenticationException;
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
public List<AuthenticationProvider> getProviders() {
return providers;
}
...
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
...
for (AuthenticationProvider provider : getProviders()) {
....
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
}
....
}
...
}
authenticate()
이다. // 1. id, pw 기반 Authentication 객체 생성, 해당 객체는 인증 여부를 확인하는 authenticated 값이 false.
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(id, password);
// 2. 검증 진행 - 내부적으로 CustomUserDetailsService.loadUserByUsername 메서드가 실행됨
Authentication authentication = authenticationManagerBuilder.getObject()
.authenticate(authenticationToken);
authenticate()
메서드를 통해 인증 로직이 수행될 때, 성공한 Authentication 객체를 생성하는 과정 중 UserDetails를 구현한 클래스의 객체가 필요하다.loadUserByUsername()
메서드를 통해 반환된다.loadUserByUsername()
만을 갖고 있는 Interface이다.public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
authenticate()
) 중 해당 메서드가 호출되며, 접근 주체의 정보에 해당하는 사용자 정보를 담은 UserDetails 객체를 반환하는 역할을 수행한다.getAuthority()
를 갖고 있는 Interface이다.public interface GrantedAuthority extends Serializable {
String getAuthority();
}
ROLE_?
형태로 활용된다.ROLE_USER
ROLE_ADMIN
getAuthorities()
메서드를 갖고 있으며, 해당 사용자가 갖고 있는 권한들을 GrantedAuthority 객체의 List로 반환해야 한다.public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
...
authenticate()
)이 수행된다. authenticate()
)authenticate()
과정 중 UserDetailsService의 loadUserByUsername()
메서드가 호출된다.loadUserByUsername()
메서드를 통해 UserDetails 객체가 생성된다.hasRole()
, hasAuthority()
등으로 설정하는 권한 정보가 해당 과정에서 반환된다.hasRole()
, hasAnyRole()
, hasAuthority()
, permitAll()
, denyAll()
, …