작성
·
255
0
Url 방식은 정삭적으로 권한계층에 맞게 manager 권한을 요청하면 manager, admin은 접근 가능하지만, user는 접근 불가하게 설정되는 반면, Method방식은 manager 권한을 요청하면 manager을 제외한 user와 admin 은 접근이 불가능합니다. 왜 이런걸까요 ?
답변 2
4
네
스프링 시큐리티는 Method 권한를 초기화 할 때 AffirmativeBased 와 AccessDecisionVoter 클래스를 기본적으로 구성하고 있습니다.
GlobalMethodSecurityConfiguration 클래스에 보시면 아래와 같은 메소드를 정의하고 있습니다.
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
if (prePostEnabled()) {
ExpressionBasedPreInvocationAdvice expressionAdvice =
new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
decisionVoters
.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
}
if (jsr250Enabled()) {
decisionVoters.add(new Jsr250Voter());
}
RoleVoter roleVoter = new RoleVoter();
GrantedAuthorityDefaults grantedAuthorityDefaults =
getSingleBeanOrNull(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaults != null) {
roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
decisionVoters.add(roleVoter);
decisionVoters.add(new AuthenticatedVoter());
return new AffirmativeBased(decisionVoters);
}
즉 AccessDecisionManager 의 구현체인 AffirmativeBased 를 생성하는 구문인데 여러가지 Voter 객체들을 생성하고 있는데 중간쯤에 보시면 RoleVoter 객체를 생성하고 있습니다.
근데 RoleVoter 는 권한 계층이 적용되지 않는 단순한 ROLE 만을 검사하는 Voter 클래스입니다.
그래서 권한 계층에 따른 리소스 접근을 적용하고자 할 때는 아래와 같은 Voter 클래스를 생성해서 추가해 주어야 합니다.
@Bean
public AccessDecisionVoter<? extends Object> roleVoter() {
RoleHierarchyVoter roleHierarchyVoter = new RoleHierarchyVoter(roleHierarchy());
return roleHierarchyVoter;
}
@Bean
public RoleHierarchyImpl roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
return roleHierarchy;
}
그래서 위의 accessDecisionManager() 메소드를 Override 해서 RoleHierarchyVoter 를 추가해 주면 됩니다.
대략 다음과 같이 구현하시면 될 것 같습니다.
@Bean
public CustomMethodSecurityInterceptor customMethodSecurityInterceptor(MapBasedMethodSecurityMetadataSource methodSecurityMetadataSource) {
CustomMethodSecurityInterceptor customMethodSecurityInterceptor = new CustomMethodSecurityInterceptor();
customMethodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
customMethodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());
customMethodSecurityInterceptor.setSecurityMetadataSource(methodSecurityMetadataSource);
RunAsManager runAsManager = runAsManager();
if (runAsManager != null) {
customMethodSecurityInterceptor.setRunAsManager(runAsManager);
}
return customMethodSecurityInterceptor;
}
@Override
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased affirmativeBased = (AffirmativeBased)super.accessDecisionManager();
List<AccessDecisionVoter<?>> decisionVoters = affirmativeBased.getDecisionVoters();
for(AccessDecisionVoter accessDecisionVoter : decisionVoters){
if(accessDecisionVoter instanceof RoleVoter){
decisionVoters.remove(accessDecisionVoter);
}
}
decisionVoters.add(0,roleVoter());
return affirmativeBased;
}
코드는 그리 복잡하지 않습니다.
먼저 CustomMethodSecurityInterceptor 빈 객체를 생성할 때 AccessDecisionManager 를 설정해 주어야 합니다.
customMethodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
그리고 아래 accessDecisionManager() 를 정의하는 구문에서 코드를 보시면 super.accessDecisionManager() 를 호출해서 기본 Voter 객체들이 담겨져 있는 List<AccessDecisionVoter<?>> decisionVoters 속성을 참조해서 기존의 RoleVoter 객체는 삭제하고 RoleHierarchyVoter 객체를 List<AccessDecisionVoter<?>> decisionVoters 의 처음 위치에 저장합니다.
그러면 최종적으로 AffirmativeBased 는 RoleHierarchyVoter 를 가진 상태가 되고 이것을 customMethodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager()) 해서 설정하면 Method 방식도 권한계층의 Voter 가 적용된 인가프로세스를 진행하게 됩니다.
실행화면입니다.
위에 보시면 리소스에 필요한 권한은 ROLE_MANAGER 인데 사용자는 ROLE_ADMIN 권한을 가지고 있습니다.
그리고 AccessDecisionManager 의 decisionVoters 에는 RoleHiearchyVoter 객체가 0번째로 위치해 있는 것을 확인할 수 있습니다.
원래는 RoleVoter 가 있었는데 초기화 시 삭제했기 때문에 없습니다.
그래서 ROLE_ADMIN 권한의 사용자는 ROLE_MANAGER 로 매핑된 리소스에 접근할 수 있게 됩니다.
결론적으로 Url 방식이든 Method 방식이든 상관없이 Voter 정책을 어떻게 하느냐에 따라 인가처리가 이루어지는 것임을 알 수 있습니다.
1