AccessDecisionManager 또는 Voter를 커스텀 해보자.
USER 사용자만 접근이 가능한 페이지
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/", "info", "/account/**").permitAll()
.mvcMatchers("/admin").hasRole("ADMIN")
.mvcMatchers("/user").hasRole("USER")
.anyRequest().authenticated();
http.formLogin();
http.httpBasic();
}
}
무식한 방법으로는 UserDetailsService에서 role을 줄때 ADMIN이면 USER도 같이 주면 된다.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findByUsername(username);
if (account == null) {
throw new UsernameNotFoundException(username);
}
return User.builder()
.username(account.getUsername())
.password(account.getPassword())
.roles(account.getRole(), "USER")
.build();
}
또는 계층 구조를 이해하는 AccessDecisionManager를 명시적으로 정의할 수 있다. 기본은 이전에 살펴본 하나라도 허용하면 허용하는 AffirmativeBased를 사용한다.
우리는 여기서도 AffirmativeBased를 사용하긴 할 것이다.
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
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<?>> voters = Collections.singletonList(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();
}
}
짜잔! ExpressionHandler를 바꿔주어서 가능하다.
코드가 장황하지만 기본 설정과 다 똑같은데 handler에 Rolehierarchy만 추가했을 뿐이다. 이 코드를 조금 줄이면 AccessDecisionManager
가 아니라 ExpressionHandler
를 커스텀 하는 것이다.