AuthenticationManager
- 스프링 시큐리티에서 인증(Authentication)은 AuthenticationManager가 처리한다.
- AuthenticationProvider 목록 중에서 인증 처리 요건에 맞는 AuthenticationProvider 를 찾아 인증처리를 위임한다.
- 아래와 같이 부모 ProviderManager 를 설정하여 AuthenticationProvider 를 계속 탐색 할 수 있다.
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
- authentication 이 유효한 인증인지 확인하고, 유효하다면 Authentication 객체를 리턴한다.
- 대부분 AuthenticationManager 인터페이스를 직접 구현한 ProviderManager 구현체 클래스를 사용한다.
- ProviderManager 를 구현한 코드를 보면 아래와 같다.
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
boolean debug = logger.isDebugEnabled();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
// ... 이하 생략
}
- 위의 코드에서 보듯 인증을 처리할 수 있는 Provider 를 순회하며, 찾는 것을 확인할 수 있다.
- authentication 에 principal , credentials 를 통해서 username 과 password 가 들어온다.
- 디버깅을 통해서 부모 클래스를 따라가다 보면 UserDetails 를 반환하는 부분을 확인 할 수 있다.
- 마지막으로는 principal 에는 User인 객체 타입으로 반환하여 Application 에서 사용할 수 있게 한다.
AuthenticationProvider
- AuthenticationProvider 는 사용자의 이름과 패스워드를 받아서 검증을 하는 클래스이다.
- AuthenticationManager 가 요청정보를 받아서 해당하는 AuthenticationProvider 를 호출한 후에 요청에 대한 인증처리를 하게된다.
- 인증이 성공게되면 인증결과를 생성해서 자신을 호출한 AuthenticationManager 에게 인증객체르 전달하는 역할까지 AuthenticationProvider 가 하게된다.
- 위와 같이 authenticate , supports 두개의 메서드가 존재한다.
- supports 에서는 폼인증 혹은 Remember Me 등을 처리할 수 있는 조건이 되는지 검증한다.
- authenticate 에서는 실질적으로 검증을 하게되고 AuthenticationManager와 동일하게 인증객체를 전달받는다.
- 인증객체 안에는 사용자가 입력한 아이디와 패스워드가 저장되어 있다. 이정보를 가지고 서버에 저장된 해당사용자의 계정과 일치하는지 검증을 하게된다. -> 존재하지 않는 다면? -> UserNotFoundException
- password 의 경우에 password encoder 클래스로 암호화해서 비교를 한다 -> 다르다면? -> BadCredentialException
AccessDecisionManager
- Access Control 결정을 내리는 인터페이스, 구현체 3가지를 기본으로 제공한다.
- AffirmativeBased : 여러 Voter 중에 한명이라도 허용하면 허용, 기본전략
- ConsensusBased : 다수결
- UnanimousBased : 만장일치
- accessDecisionManager() 를 따로 구현하지 않으면, 기본적으로 AffirmativeBased() 를 사용한다.
- 아래와 같이 AffirmativeBased 코드에서 voter에 의해서 result 값이 생성된다.
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
// ... 이하 생략
}
AccessDecisionVoter
- AccessDecisionVoter 는 3가지 역할을 한다.
- Authentication이 특정한 Object에 접근할 때 필요한 ConfigAttribute를 만족하는지 확인
- WebExpressionVoter : 웹 시큐리티에서 사용하는 기본 구현체, ROLE_XXX 일치하는지 확인
- RoleHierarchyVoter : 계층형 Role 지원
- 아래와 같이 계층을 설정할 수 있다.
public AccessDecisionManager accessDecisionManager() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER"); DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler(); handler.setRoleHierarchy(roleHierarchy); WebExpressionVoter webExpressionVoter = new WebExpressionVoter(); webExpressionVoter.setExpressionHandler(handler); List<AccessDecisionVoter<? extends Object>> voters = Arrays.asList(webExpressionVoter); return new AffirmativeBased(voters); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .mvcMatchers("/", "/info", "/account/**").permitAll() .mvcMatchers("/admin").hasRole("ADMIN") .mvcMatchers("/user").hasRole("USER") .anyRequest().authenticated() .accessDecisionManager(accessDecisionManager()); http.formLogin(); http.httpBasic(); }
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
여기서 roleHierarchy 를 설정해준다.- 그 다음 기존에 쓰는 handler 를 로드하고, setRoleHierarchy 를 해준다.
REFERENCES
- 백기선님의 스프링 시큐리티
- 정수원님의 스프링 시큐리티
'Spring Security' 카테고리의 다른 글
WebAuthenticationDetails 와 AuthenticationDetailsSource (0) | 2022.02.28 |
---|---|
CustomAuthenticationProvider (0) | 2022.02.28 |
Security Config (0) | 2022.02.28 |
SecurityContextHolder 와 FilterChainProxy (0) | 2022.02.28 |
SecurityContextHolder, AuthenticationManager, ThreadLocal (0) | 2022.02.28 |