Skip to content
This repository was archived by the owner on Mar 2, 2023. It is now read-only.

4. Method Security Expression

강교일(Mambo) edited this page Mar 11, 2020 · 1 revision

본 페이지는 스프링 시큐리티가 지원하는 메소드 보안 표현식에 대해서 설명합니다.

표현식 기반의 접근 제어

스프링 시큐리티 3.0+부터 Spring EL 표현식을 사용해 권한 부여 매커니즘을 적용할 수 있습니다. 표현식 기반의 접근 제어는 동일한 아키텍처를 기반으로 하지만 복잡한 Boolean 로직을 단일 표현식으로 캡슐화 합니다.

일반 내장된 표현식

SecurityExpressionRoot는 최상위 표현식 오브젝트를 위한 기초 클래스입니다. 이 클래스는 웹 과 메소드 보안에 사용할 수 있는 몇가지 일반 표현식을 제공합니다.

Table 1. 일반 내장 표현식

표현식 설명
hasRole(String role) 현재 인증 주체가 특정 롤을 가지면 true를 리턴합니다.
예를 들어, hasRole('admin')
기본적으로 적용된 롤이 'ROLE_'로 시작하지 않으면 'ROLE_'이 앞에 추가됩니다. DefaultWebSecurityExpressionHandlerdefaultRolePrefix를 수정해서 커스터마이징 할 수 있습니다.
hasAnyRole(String... roles) 현재 인증 주체가 적용된 롤들 중에서 하나 이상을 가지면 true를 리턴합니다.
예를 들어, hasAnyRole('admin', 'user')
hasAuthority(String authority) 현재 인증 주체가 특정 권한을 가지면 true를 리턴합니다.
예를 들어, `hasAuthority('read')
hasAnyAuthority(String... authorities) 현재 인증 주체가 적용된 권한 중에서 하나 이상을 가지만 true를 리턴합니다.
예를 들어, hasAnyAuthority('read', 'write')
principal 현재 사용자를 표현하는 인증 주체 오브젝트에 직접적으로 접근할 수 있습니다.
authentication SecurityContext으로부터 현재 Authentication 오브젝트를 얻어 직접적으로 접근할 수 있습니다.
permitAll 항상 true로 평가합니다.
denyAll 항상 false로 평가합니다.
isAnonymous() 현재 인증 주체가 익명의 사용자인 경우 true를 리턴합니다.
isRememberMe() 현재 인증 주체가 리멤버-미 사용자인 경우 true를 리턴합니다.
isAuthenticated() 현재 사용자가 익명이 아닌 경우 true를 리턴합니다.
isFullyAuthenticated() 현재 사용자가 익명이 아니거나 리멤버-미 사용자이면 true를 리턴합니다.
hasPermission(Object target, Object permission) 현재 사용자가 주어진 퍼미션에 대해 제공된 타겟에 접근할 수 있으면 true를 리턴합니다.
hasPermission('domainObject', 'read')
hasPermission(Object targetId, String targetType, Object permission) 현재 사용자가 주어진 퍼미션에 대해 제공된 타겟에 접근할 수 있으면 true를 리턴합니다.
hasPermission(1, 'com.example.domain.Message', 'read')

웹 보안 표현식

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").access("hasRole('admin') and hasIpAddress('192.168.1.0/24')");
}

"admin" 영역("/admin/**")을 "admin" 롤이 부여된 사용자이면서 IP주소가 로컬 서브넷과 일치하여야 사용할 수 있도록 정의했습니다. 표현식 hasIpAddress는 웹 보안에서 사용할 수 있는 추가 내장 표현식입니다. 이 표현식은 WebSecurityExpressionRoot 클래스에 정의되어 있습니다. 이 오브젝트는 HttpServletRequest 객체를 직접 노출하여 표현식에서 request를 직접적으로 호출할 수 있습니다. 표현식을 사용하면 AccessDecisionManagerWebExpressionVoter가 추가됩니다.

웹 보안 표현식에서 Bean 참조

사용 가능한 표현식을 확장하고싶은 경우 공개된 모든 Spring Bean을 쉽게 참조할 수 있습니다. 예를 들어, 다음의 메소드 시그니처를 포함하는 webSecurity 이름의 빈을 가진다고 가정합시다.

public class WebSecurity {
     public boolean check(Authentication authentication, HttpServletRequest request) {
             ...
     }
}

함수를 사용해서 참조할 수 있습니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/users/**").access("@webSecurity.check(authentication, httpServletRequest)");
}

웹 보안 표현식에서의 경로 변수

때때로는 URL 안에 있는 경로 변수를 참조하는게 좋을 수 있습니다. 예를 들어, RESTful 애플리케이션이 /users/{userId} 경로에서 사용자 ID로 사용자를 찾는다면 패턴에 위치한 경로 변수를 쉽게 참조할 수 있습니다.

public class WebSecurity {
    public boolean checkUserId(Authentication authentication, long id) {
        return false;
    }
}

checkUserId 메소드로 매치된 경로 변수를 제공합니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/users/{userId}").access("@webSecurity.checkUserId(authentication, #userId)");
}

메소드 보안 표현식

메소드 보안은 간단한 허용 또는 거부 규칙보다 조금 더 복잡합니다. 스프링 시큐리티 3.0+는 포괄적인 표현식 사용을 위해서 몇가지 새로운 어노테이션을 소개했습니다.

@PreAuthorize와 @PostAuthorize를 사용하여 접근 제어

가장 명백하게 유용한 어노테이션은 @PreAuthorize로 메소드를 실제로 호출할 수 있는지 여부를 결정합니다. 예를 들어,

@PreAuthorize("hasRole('USER')")
public void create(Contact contact);

이 의미는 "ROLE_USER" 롤을 가진 사용자만 접근을 허용한다는 의미입니다.

모든 Spring EL 기능은 표현식 내에서 사용가능하므로 매개변수의 프로퍼티에 접근할 수 있습니다. 예를 들어, 사용자의 연락처와 일치하는 사용자만 접근을 허용하는 어떤 함수가 있다면

@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);

예를 들어, 게시물의 작성자와 현재 인증 주체가 동일한지 비교할 수 있겠네요

메소드 보안 메타 어노테이션

메소드 보안을 위해 메타 어노테이션을 사용해서 코드를 더 읽기 쉽게 만들 수 있습니다. 이는 특히 코드 베이스 전체에서 동일하게 사용하는 복잡한 표현식의 반복에 편리합니다. 예를 들어,

@PreAuthorize("#contact.name == authentication.name")

모든 곳에서 반복하는 것 대신에 메타 어노테이션을 만들어서 대신 사용할 수 있습니다.

@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("#contact.name == authentication.name")
public @interface ContactPermission {}

메소드 보안

메소드에 대한 보안을 지원하기 위해서 스프링 시큐리티가 2.0+ 부터 추가된 보안 적용 방식입니다. @Secured 어노테이션으로 알려진 JSR-250 어노테이션에 대한 지원을 제공합니다. 또한, 3.0+부터는 새로운 표현식 기반의 어노테이션을 추가했습니다.

EnableGlobalMethodSecurity

@Configuration 인스턴스에 @EnableGlobalMethodSecurity를 사용해서 어노테이션 기반의 보안을 활성화 할 수 있습니다.

  • securedEnabled : @Secured 어노테이션을 활성화 합니다.
  • jsr250Enabled : JSR-250 어노테이션을 활성화합니다.
  • prePostEnabled : Spring Security의 기본 어노테이션을 활성화합니다.
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = false)
@Configuration(proxyBeanMethods = false)
public class GlobalMethodSecurityConfig {

}

GlobalMethodSecurityConfiguration

@EnableGlobalMethodSecurity 어노테이션이 허용하는 것보다 더 복잡하게 수행하기 위해서 GlobalMethodSecurityConfiguration를 확장 할 수 있습니다.

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = false)
@Configuration(proxyBeanMethods = false)
public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        // ... create and return custom MethodSecurityExpressionHandler ...
        return expressionHandler;
    }
}

참고