레퍼런스 번역/Spring Security

Spring Security (5.1.9.RELEASE) Section 11 Authorization

북치던노인 2019. 8. 20. 15:45

11. Authorization

11.1. Authorization Architecture

11.1.1. Authorities

Authentication 구현체는 GrantedAuthority 오브젝트의 리스트를 저장한다. 이 값들은 AuthenticationManager에 의해 세팅되며, AccessDecisionManager에 의해 인가 결정을 내릴때 읽힌다.

GrantedAuthority는 1개 메서드를 갖는 인터페이스 이다.

String getAuthority();

이 메서드는 AccessDecisionManager가 GrantedAuthority의 정확한 String 표현식을 획득하도록 해준다. 만약 GrantedAuthority가 정확한 String으로 변환 될 수 없다면, GrantedAuthority는 "복잡한" 형태로 간주되고, getAuthority()는 반드시 null을 반환해야 한다.

"복잡한" GrantedAuthority" 예시를 들자면, 다른 고객 번호에 적용되는 연산자 및 권한 임계치 목록을 저장하는 구현체를 생각해 볼 수 있다.

Spring Security는 GrantedAuthority의 구체적인 구현체로, SimpleGrantedAuthority를 포함하고 있다. 이것은 임의의 사용자 지정 문자열을 GrantedAuthority로 변환되도록 해준다. 모든 AuthenticationProvider들은 Authentication 객체를 만들어 내기 위해 SimpleGrantedAuthority를 사용하는 보안 아키텍처를 포함하고 있다.

11.1.2 Pre-Invocation Handling

Technical Overview 챕터에서 살펴봤던 것 처럼, Spring Security는 보호된 객체 - 메소드 실행 또는 웹 리퀘스트 - 에 대한 인터셉터들을 제공하고 있다. 실행을 허가할지 여부는 AccessDecisionManager에 의해 결정된다.

The AccessDecisionManager

AccessDecisionManager는 AbstractSecurityInterceptor에 의해 호출되며, 이것은 접근 제어 결정에 대한 책임을 담당한다. AccessDecisionManager 인터페이스는 세가지 메서들을 포함하고 있다.

void decide(Authentication authentication, Object secureObject,
    Collection<ConfigAttribute> attrs) throws AccessDeniedException;

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

AccessDecisionManager의 decide 메서드는 인가 결정에 필요한 모든 정보를 전달 받는다. 특별히, secureObject 전달을 통해 실제 secure object 실행에 포함되는 인자들이 검사될수 있다. 예를 들어, secure object이 MethodInvocation이라고 가정해보자. 어

어떤 Customer 인자에 대해서든 MethodInvocation을 질의하는것이나, 특정 principle이 해당 Customer에 대한 연산을 처리하는 것을 허용할지에 대한 AccessDecisionManager에서의 일련의 보안 로직을 구현하는 것은 쉬울 것이다. 만약 접근이 거부되는 경우, AccessDeniedException 예외를 던지도록 구현해야 한다.

supports(ConfigAttribute) 메서드는 전달된 ConfigAttribute에 대해 AccessDecisionManager가 처리 가능한지 여부를 결정하는 시작 시점에 AbstractSecurityInterceptor에 의해 호출된다. supports(Class)메서드는 secure object의 type을 지원하는 AccessDecisionManager가 존재하는지를 확인하기 위해 security interceptor 구현에 의해 호출된다.

Voting-Based AccessDecisionManager Implementations

유저가 인가의 모든 측면을 제어할수 있는 AccessDecisionManager를 구현 할 수 있지만, Spring Security는 투표에 기반한 몇가지 AccessDecisionManager 구현을 포함하고 있다.

Voting Decision Manager

이 접근 방식으로, 일련의 AccessDecisionVoter 구현체가 인가 결정 과정에 투표를 한다. 그 다음 AccessDecisionManager은 투표 결과에 따라 AccessDeniedException을 던질지 말지 결정한다.

AccessDecisionVoter 인터페이스는 세개의 메서드를 가진다:

int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs);

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

구체적인 구현으로는 int를 리턴하지만, 가능한 value들은 AccessDecisionVoter에 static 필드로 제공된다(ACCESS_ABSTAIN, ACCESS_DENIED, ACCESS_GRANTED). 인가 결정 과정에 아무런 의견도 내고싶지 않으면 ACCESS_ABSTAIN을 리턴한다. 의견이 있으면 ACCESS_DENIED 또는 ACCESS_GRANTED을 반드시 리턴해야 한다.

투표 결과를 집계하는 데에는 세개의 구체적인 AccessDecisionManager가 제공된다. ConsensusBased 구현은 기권투표를 제외한 합의 결과에 따른다. 동일득표 또는 모두 기권일 경우에 할 행동을 결정하는 프로퍼티가 제공된다. AffirmativeBased 구현은 1개 이상의 허가 득표를 얻었을 경우 인가해주는 구현이다. ConsensusBased 구현과 동일하게, 모든 득표가 무효일 때 취할 행동을 결정하는 파라미터가 있다. UnanimousBased는 기권을 제외한 만장일치에만 인가된다. 하나라도 ACCESS_DENIED 표가 있다면 접근 거부된다. 다른 구현들 처럼, 모든 표가 기권일때 어떻게 행동할지를 결정하는 파라미터가 있다.

다르게 투표 결과를 집계하는 커스텀 AccessDecisionManager를 구현 할 수 있다. 예를 들어, 가중치가 부여되면서 특정 참여자에겐 특별한 거부권을 부여하는 형태가 있을 수 있다.

RoleVoter

제일 일반적으로 쓰이는 AccessDecisionVoterRoleVoter인데, configuration attribute들을 role name으로 간주하고 유저가 해당 롤이 부여되어 있을 경우 grant access에 투표한다.

이 방식은 ROLE_ 로 시작되는 ConfigAttribute이 있으면 투표한다. 만약 문자열 표현(getAuthority() 메서드를 통해)을 리턴하는 GrantedAuthority가 ROLE_로 시작되는 ConfigAttribute와 한 개 이상 정확하게 일치할 경우 인가에 투표 할 것이다. ROLE_로 시작하는 정확하게 일치하는 ConfigAttribute가 하나도 없으면 인가 거부에 투표한다. ROLE_로 시작하는 ConfigAttribute가 없으면 기권에 투표한다.

AuthenticatedVoter

AuthenticatedVoter는 익명 / 완전히 인증된 유저 / remember-me 인증 유저의 상황에서 다르게 사용된다. 다수의 사이트에서 remember-me 인증 상태에서는 접근을 제한하고 완전한 접근을 위해선 로그인을 확인하도록 요구한다.

익명 접근을 허가하기 위해 IS_AUTHENTICATED_ANONYMOUSLY 속성을 사용하면 이 속성은 AuthenticatedVoter에 의해 처리된다. 추가 정보는 Javadoc 참조.

Custom Voters

커스텀 AccessDecisionVoter를 구현하여 어떠한 유형의 접근 제어 로직이든 원하는대로 추가 할 수 있다. 비지니스 로직과 관련되어 있을수도 있고, 관리자 로직과 관련되어 있을수도 있다. 예를들어, 보류 상태인 계정에서 접근 거부를 위해 어떻게 voter를 사용해야 하는지에 관한 Spring 웹사이트 블로그 문서를 찾아 볼 수 있을 것이다.

11.1.3. After Invocation Handling

AccessDecisionManager는 AbstractSecurityInterceptor에 의해서 secure object 접근 전에 호출되는 반면, 어떤 애플리케이션은 secure object 호출에 의해 리턴되는 결과 객체를 수정해야 하는 방법이 필요할수도 있다. 직접 AOP 로 이를 구현할 수도 있겠지만, Spring Security는 ACL 처리능력과 함께 결합한 구체적인 구현체를 포함한 편리한 hook들을 제공한다.

Spring Security의 AfterInvocationManager와 그 구체적인 구현체를 묘사한 After Invocation Implementation
After Invocation Implementation

Spring Security의 다른 부분들과 마찬가지로, AfterInvocationManager는 한개의 구체적인 구현체인 AfterInvocationProviderManager를 가지고 있고, 이것은 AfterInvocationProvider들에게 질의한다. 각각의 AfterInvocationProvider들은 리턴 객체를 수정하거나 AccessDeniedException을 던지도록 허용된다. 여러개의 provider들이 각각 리스트 순서대로 전달받으면서 차례로 전달받아가면서 객체를 수정 할 수 있다.

만약 AfterInvocationManager를 사용하고 있다면 MethodSecurityInterceptor의 AccessDecisionManager에게 작업을 허용하도록 하는 설정 속성들이 필요하다는 것을 알 필요가 있따. 만약 전형적인 AccessDecisionManager 구현체를 포함하는 Spring Security를 사용하고 있으면서, 특정 메소드 실행 보안을 위한 설정 속성을 가지고 있지 않다면 각각의 AccessDecisionVoter들이 무효표를 던지도록 만들 것이다. 차례로, AccessDecisionManager의 속성인 “allowIfAllAbstainDecisions” 가 false이면, AccessDeniedException이 던져질 것이다. 이 잠재적인 이슈를 피하기 위해선 (i) (일반적으로 추천되진 않지만) “allowIfAllAbstainDecisions”을 true로 하거나, (ii) AccessDecisionVoter이 접근 허가에 투표하도록 하는 최소한의 설정이 주어져 있도록 보장할 수 있다. 후자(추천됨) 방법은 ROLE_USER나 ROLE_AUTHENTICATED 설정을 통해 얻어질 수 있다.

11.1.4. Hierarchical Roles

특정 role이 다른 role을 포함하도록 하는 것은 일반적인 요구사항이다. 예를 들어, admin과 user role을 가진 애플리케이션이 있고, admin은 일반 user가 할 수 있는 모든 권한을 가진다고 하자. 이런 구조화된 권한을 위해서는 RoleHierarchyVoter 이라는 RoleVoter를 사용하면 된다. RoleHierarchyVoter는 RoleHierarchy와 함께 설정된다. 일반적은 구성은 아래와 같다:

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_STAFF
            ROLE_STAFF > ROLE_USER
            ROLE_USER > ROLE_GUEST
        </value>
    </property>
</bean>

여기서 우리는 4개의 role을 가진다. ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST

11.2. Secure Object Implementations

11.2.1. AOP Alliance (MethodInvocation) Security Interceptor

Method security는 MethodSecurityInterceptor에 의해 강제되어 MethodInvocation들을 보호하게 된다. 설정에 의해, 인터셉터는 하나의 빈에 대해서일수도 있고 여러개의 빈에 공유될수도 있다. 인터셉터 인스턴스는 특정 메서드 실행에 적용되는 configuration attribute를 획득하기 위해 MethodSecurityMetadataSource를 사용한다. 메서드 네임(와일드 카드 가능)을 키로 사용하는 configuration attribute들을 저장하기 위해서는 MapBasedMethodSecurityMetadataSource가 쓰이며, 이것들은 항목들을 사용하는 application context 안에 attribute가 정의되어 있을 때 내부적으로 사용된다.
(뭔소리여..)

Explicit MethodSecurityInterceptor Configuration

application context에서 Spring AOP의 프록싱 메커니즘 중 하나를 사용하기 위해선 MethodSecurityInterceptor를 설정할수도 있다.

<bean id="bankManagerSecurity" class=
    "org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
    <sec:method-security-metadata-source>
    <sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
    <sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
    </sec:method-security-metadata-source>
</property>
</bean>

11.2.2. AspectJ (JoinPoint) Security Interceptor

AspectJ security interceptor는 이전 섹션에서 살펴본 AOP Alliance security interceptor와 매우 유사하다. 차이점만 살펴보자.

AspectJ 인터셉터는 이름이 AspectJSecurityInterceptor 이다. AOP Alliance security interceptor와는 다르게, 이것은 ...
(생략)

<bean id="bankManagerSecurity" class=
    "org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
    <sec:method-security-metadata-source>
    <sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
    <sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
    </sec:method-security-metadata-source>
</property>
</bean>

.. 생략

package org.springframework.security.samples.aspectj;

import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.AspectJCallback;
import org.springframework.beans.factory.InitializingBean;

public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {

    private AspectJSecurityInterceptor securityInterceptor;

    pointcut domainObjectInstanceExecution(): target(PersistableEntity)
        && execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect);

    Object around(): domainObjectInstanceExecution() {
        if (this.securityInterceptor == null) {
            return proceed();
        }

        AspectJCallback callback = new AspectJCallback() {
            public Object proceedWithObject() {
                return proceed();
            }
        };

        return this.securityInterceptor.invoke(thisJoinPoint, callback);
    }

    public AspectJSecurityInterceptor getSecurityInterceptor() {
        return securityInterceptor;
    }

    public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
        this.securityInterceptor = securityInterceptor;
    }

    public void afterPropertiesSet() throws Exception {
        if (this.securityInterceptor == null)
            throw new IllegalArgumentException("securityInterceptor required");
        }
    }
}

.. (생략)

<bean id="domainObjectInstanceSecurityAspect"
    class="security.samples.aspectj.DomainObjectInstanceSecurityAspect"
    factory-method="aspectOf">
<property name="securityInterceptor" ref="bankManagerSecurity"/>
</bean>

.. 생략

11.3. Expression-Based Access Control

Spring Security 3.0은 Spring EL 표현식으로의 인가 메커니즘 및 투표기반 접근 결정 방식을 위한 configuration attribute을 도입했다. Expression-based 접근 제어는 동일한 아키텍처로 만들어졌지만 하나의 표현식에 복잡한 로직을 캡슐화 하는 것이 허용된다.

11.3.1. Overview

표현식의 root 객체는 SecurityExpressionRoot이다. web과 method 보안에 둘다 쓰인다.

Common Built-In Expressions

Expression Description
hasRole([role]) 현재 principal이 주어진 role을 가지고 있는지 여부를 반환. ROLE_ 은 생략 가능. 이 동작은 DefaultWebSecurityExpressionHandler의 defaultRolePrefix 설정에 의해 변경 가능
hasAnyRole([role1,role2]) hasRole와 동일하지만 여러개 지정 가능. 그중 하나 가지고 있으면 true 리턴
hasAuthority([authority]) role 말고 authority로 지정
hasAnyAuthority([authority1,authority2]) 생략
principal current user에 대한 principal 객체로의 직접 접근
authentication 현재 SecurityContext의 Authentication 객체 직접 접근
permitAll 항상 true
denyAll 항상 false
isAnonymous() 현재 principal이 anonymouse user이면 true
isRememberMe() 현재 principal이 remember-me user이면 true
isAuthenticated() 익명유저가 아니면 true
isFullyAuthenticated() 익명유저도 아니고 remember-me유저도 아니어야 true
hasPermission(Object target, Object permission) hasPermission(domainObject, 'read')
hasPermission(Object targetId, String targetType, Object permission) hasPermission(1, 'com.example.domain.Message', 'read')

11.3.2. Web Security Expressions

개별 URL을 보호하기 위해 표현식을 쓰려면, 먼저 http 항목의 use-expressions 속성을 true로 주어야 한다. 그러면 intercept-url에 Spring EL 표현식을 포함한 access 속성을 받을 수 있다.

<http>
    <intercept-url pattern="/admin*"
        access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
    ...
</http>

여기에서는 "admin"권한을 가지고 있고 IP 주소가 로컬 서브넷과 일치하는 사용자만 애플리케이션의 "admin"영역(URL 패턴으로 정의 됨)을 사용할 수 있도록 정의했다. 표현식 hasIpAddress은 web security에 특정된 추가적인 빌트인 표현식이다. 이것은 web access 표현식을 평가 할 때 expression root object로 사용되는 WebSecurityExpressionRoot 클래스에 정의되어 있다. 이 객체는 HttpServletRequest를 request 이름 아래에 직접적으로 노출하고 있어서, 표현식에서 request를 직접 호출할 수 있다. 만약 표현식이 사용되면, namespace에 의해 사용되는 AccessDecisionManager에 WebExpressionVoter가 추가된다. 그래서 만약 namespace를 사용하지 않고 이런 표현식을 사용하고 싶다면, 설정에 이것을 꼭 추가 해야 한다.

Referring to Beans in Web Security Expressions

사용 가능한 표현식을 확장하길 원한다면, 노출된 어느 Spring Bean이라도 직접 참조 할 수 있다. 예를 들어 webSecurity 란 이름을 가진 bean을 가지고 있고 아래와 같은 메서드 시그니처가 있다고 하면:

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

아래와 같은 방법으로 참조할 수 있다.

<http>
    <intercept-url pattern="/user/**"
        access="@webSecurity.check(authentication,request)"/>
    ...
</http>

자바 설정으로는 이렇게..

http
        .authorizeRequests()
                .antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
                ...

Path Variables in Web Security Expressions

패턴에 path variable을 쉽게 참조할수도 있다. 예를 들어, webSecurity란 이름의 bean을 가지고 있고 아래와 같은 메서드 시그니처가 있다고 치면:

public class WebSecurity {
      public boolean checkUserId(Authentication authentication, int id) {
              ...
      }
}

아래와 같이 참조 할 수 있다.

<http>
    <intercept-url pattern="/user/{userId}/**"
        access="@webSecurity.checkUserId(authentication,#userId)"/>
    ...
</http>

자바 설정으론 이렇게:

http
        .authorizeRequests()
        .antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
                ...

/user/123/resource 인 경우 전달되는 id는 123이 될 것이다.

11.3.3. Method Security Expressions

메서드 보안은 단순히 allow / deny 보단 좀 더 복잡하다.

@Pre and @Post Annotations

네가지 annotation이 있다. @PreAuthorize, @PreFilter, @PostAuthorize, @PostFilter. global-method-security namespace 항목에 의해 활성화 된다.

<global-method-security pre-post-annotations="enabled"/>
Access Control using @PreAuthorize and @PostAuthorize

@PreAuthorize는 메서드가 호출될수 있을지 없을지를 결정한다

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

그런데..

@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);

위와 같은 표현식은 application context의 Spring Security ACL 모듈에 링크되어 있다. 좀 더 나중에 살펴보자. expression variables 라는 이름으로 메서드 인자들을 접근 할 수 있다.

Spring Security에는 메서드 인자들을 처리할 수 있는 다양한 방법들이 있다. Spring Security는 파라미터 이름을 알아내기 위해 DefaultSecurityParameterNameDiscoverer를 사용한다. 기본값으로, 아래 옵션들이 사용된다.

  • 만약 Spring Security의 @P 어노테이션이 하나의 인자에 존재하면, 해당 값이 사용된다.
    import org.springframework.security.access.method.P;
    ...
    PreAuthorize("#c.name == authentication.name")
    public void doSomething(@P("c") Contact contact);
    AnnotationParameterNameDiscoverer을 사용하여 구현되었다.
  • Spring Data의 @Param 어노테이션이 최소 한개의 파라미터에 존재하면, 해당 값이 사용된다.
    import org.springframework.data.repository.query.Param;
    ...  
    @PreAuthorize("#n == authentication.name")
    Contact findContactByName(@Param("n") String name);
    AnnotationParameterNameDiscoverer을 사용하여 구현되었다.
  • 소스 컴파일에 JDK8 및 -parameters 인자가 사용되었고 Spring 4+가 사용되었다면 파라미터 이름을 알아내기 위해 JDK reflection API가 사용된다. 이것은 클래스와 인터페이스 모두에 동작한다.
  • 마지막으로, debug 심볼과 함께 소스가 컴파일 되었다면, 파라미터 이름은 debug 심볼에 의해 찾아내질 것이다. 이 경우 인터페이스에는 동작하지 않을 것이다. 왜냐면 거기엔 파라미터 이름에 대해 디버그 정보가 없을 것이기 때문이다. 인터페이스에 대해서는 어노테이션 또는 JDK 8 접근법을 사용해야한다.

어떤 Spring-EL 기능도 표현식 내에 가능하기 때문에, 인자의 속성 또한 접근 가능하다. 예를 들면 요렇게..

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

여기선 security context 내에 저장된 빌트인 표현식인 authentication를 사용했다. "principal" 속성도 "principal" 표현을 통해 직접 접근 가능하다. 실제 값은 UserDetails 인스턴스에 의해 획득되므로 principal.username이나 principal.enabled 표현을 사용할수 있다.

일반적이진 않지만, 메서드 호출 이후에 접근 제어를 수행하길 바랄 수 있다. 이것은 @PostAuthorize 어노테이션을 통해 할 수 있다. 리턴된 값에 접근하려면, returnObject 빌트인 표현을 사용해라.

Filtering using @PreFilter and @PostFilter

리턴된 collection이나 array에 대해 필터링을 걸 수 있다. (흔함)

@PreAuthorize("hasRole('USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();

@PostFilter를 쓰면 Spring Security는 리턴된 컬렉션을 순환하며 해당 표현식이 false인 항목을 걸러낸다. filterObject는 컬렉션 안의 현재 객체를 나타낸다. 메서드 시작시에도 @PreFilter를 통해 할 수 있다(흔하진 않음). 문법은 똑같지만, 만약 인자가 한개보다 많을 경우 filterTarget 속성을 통해 어느 인자인지를 지정해 줘야 한다.

따로 튜닝되진 않으므로 비효율 적인 처리가 될 수 있다.

Built-In Expressions

앞에서 살펴본 것 처럼 메서드 보안에만 적용되는 빌트인 표현식이 좀 있다. filterTarget이나 returnValue 값은 단순명료 하지만, hasPermission() 표현은 더 자세히 봐야할 필요가 있다.

The PermissionEvaluator interface

hasPermission() 표현식은 PermissionEvaluator 인스턴스에 위임된다. 이것은 표현식 시스템과 Spring Security의 ACL 시스템 사이의 가교 역할을 의도하고 있어서, 추상적인 permission들에 기반해 도메인 객체에 대한 인가 제한을 기술할 수 있도록 해준다. ACL 모둘에 명시적인 의존성은 없어서, 필요에 따라 대안적인 구현으로 대체 할 수 있다. 인터페이스는 두개의 메서드를 갖는다:

boolean hasPermission(Authentication authentication, Object targetDomainObject,
                            Object permission);

boolean hasPermission(Authentication authentication, Serializable targetId,
                            String targetType, Object permission);

첫번째 인자(Authentication 객체)는 제외하고, 표현식의 가능한 버전들에 직접적으로 매핑된다. 첫번째것은 접근이 제어 될 도메인 객체가 이미 로드 되었을 때 쓰인다. 그리고 현재 유저가 그 객체에 대해 권한이 있으면 true를 리턴한다. 두번째버전은 객체가 아직 로드되진 않았지만, 식별자는 알 수 있을 때 쓰인다. 로드되어야 할 알맞은 ACL permission를 허용할 도메인 객체의 'type' 이 요구된다. 이것은 전통적으로 객체의 Java class 이어 왔지만, 권한이 로드되는 방식과의 일치여부에 따라 꼭 그럴 필요는 없다.

hasPermission() 표현식을 사용하기 위해, application context에 PermissionEvaluator가 명식적으로 설정되어야 한다. 아래와 같이..

<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>

<bean id="expressionHandler" class=
"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator" ref="myPermissionEvaluator"/>
</bean>

myPermissionEvaluatorPermissionEvaluator를 구현한 bean이다. 대개 이것은 AclPermissionEvaluator로 불리는 ACL module 구현체가 된다. 자세한 내용은 'Contacts' 샘플 애플리케이션 참조.

Method Security Meta Annotations

코드의 가독성을 향상시키기 위해 meta annotation을 쓸수도 있다. 반복되는 복잡한 표현식이 있다면 특별히 유용하다. 예를들면 이렇다

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

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

위와같이 할수있다. 단, JSR-250 스펙은 지원되지 않음