[Spring Security] Spring Security Basic - GlobalMethodSecurityConfiguration

들어가며

  • GlobalMethodSecurityConfiguration는 Method level에 Security를 설정할 수 있게 해준다.
  • 즉, web service가 아니더라도 security를 적용하여 ROLE 단위에 사용가능한 method를 설정할 수 있다.

어떤 역할을 하는가?

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
public class MethodSecurity extends GlobalMethodSecurityConfiguration {

}
  • @EnableGlobalMethodSecurity를 통해서 enable 상태로 변경해야 한다.
  • 그 중에서 사용하고 싶은 방법 중 원하는 것을 선택해서 활성화 하고 사용하면 된다.
  • 추가로 설정을 하고 싶다면 GlobalMethodSecurityConfiguration을 상속해서 오버라이드해서 사용하면 된다.
  • @Secured, [@PreAuthorize, @PostAuthorize], @RolesAllowed로 3가지 방법이 존재한다.

JSR-250 방식

  • JSR-250 방식은 javax.annotation.security에 존재하는 annotation을 이용해서 security를 설정하는 방식이다.
  • 3가지 annotation을 사용할 수 있는데 @PermitAll, @DenyAll, @RolesAllowed 방식이 존재한다.
@RolesAllowed({"ROLE_USER","ROLE_ADMIN"})
//@PermitAll
//@DenyAll
public void dashboard() {
  Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  Object principal = authentication.getPrincipal();
  Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
  Object credentials = authentication.getCredentials();
  boolean authenticated = authentication.isAuthenticated();
}
  • 해당 방식처럼 method에 annotation을 설정해서 사용하면 된다.
  • PermitAll, DenyAll은 말 그대로 전체허용, 거부 이고, RolesAllowed는 value에 전달된 ROLE을 검사해서 확인하게 된다.
  • Jsr250Voter에서 PermitAll, DenyAll에 대해서 확인 후 처리 한다.

secured 방식

public interface BankService {
  @Secured("IS_AUTHENTICATED_FULLY")
  public Account readAccount(Long id);
  @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  public Account[] findAccounts();
  @Secured({"ROLE_USER", "ROLE_ADMIN"})
  public Account post(Account account, double amount);
}
  • @Secured를 이용해서 사용하면 되고, ROLE단위에 매칭도 가능하고 좀 더 다양한 기능을 지원한다.
  • 해당 방식은 RoleVoter, AuthenticatedVoter에서 값을 확인하고 처리하게 된다.
  • RoleVoterROLE_로 시작하는 경우 확인하게 되고, AuthenticatedVoter에서 IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_REMEMBERED에 대해서만 검증을 한다.

prePost 방식

public interface BankService {
  @PreAuthorize("isAnonymous()")
  public Account readAccount(Long id);

  @PreAuthorize("isAnonymous()")
  public Account[] findAccounts();

  @PreAuthorize("hasAuthority('ROLE_TELLER')")
  public Account post(Account account, double amount);
}
  • @PreAuthorize, @PostAuthorize를 통해서 method가 실행되기 전, 실행 후에 authorize를 확인할 수 있다.
  • 앞에 방식보다 좀 더 Spel을 사용할 수 있고, hasRole('ROLE_USER') 처럼 사용해서 ROLE 단위에 매칭도 사용 가능하다.
@PostAuthorize
  ("returnObject.username == authentication.principal.nickName")
public CustomUser loadUserDetail(String username) {
    return userRoleRepository.loadUserByUserName(username);
}

@PreAuthorize("#username == authentication.principal.username")
public String getMyRoles(String username) {
    //...
}
  • returnObject와 paramater를 바탕으로도 설정을 제한할 수 있어서 좀 더 다양하게 활용될 수 있다.
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
public class MethodSecurity extends GlobalMethodSecurityConfiguration {
//    @Override
//    protected AccessDecisionManager accessDecisionManager() {
//        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
//        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
//        AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
//        accessDecisionManager.getDecisionVoters().add(new RoleHierarchyVoter(roleHierarchy));
//
//        return accessDecisionManager;
//    }

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        final DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        final RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
        handler.setRoleHierarchy(roleHierarchy);
        return handler;
    }
}
  • PreInvocationAuthorizationAdviceVoter에서만 MethodSecurityExpressionHandler를 이용해서 RoleHierarchy를 적용할 수 있다.
  • GlobalMethodSecurityConfiguration를 상속해서 method를 override하고 expressionHandler에 RoleHierarchy를 설정한다.
  • 해당 방식으로 하게 되면 Pre, Post에서만 사용가능 하나, 주석처리되어 있는 방식으로 하게 되면 AccessDecisionManager에 voter를 추가하기 때문에 설정과 무관하게 roleHierarchy를 적용할 수 있다.

마치며

  • 대부분 web security를 활용해서 제한을 많이 하지만, 만약 PC용 프로그램에 제한을 걸어야 한다면 method security를 이용해서 제한을 걸어도 괜찮아보인다.
  • 그 외에도 request matcher 방식이 아닌 annotation 방식을 이용해서 제한을 하고 싶을 때도 사용이 가능해 보여서 알아두면 좋은 것 같다.

Leave a comment