Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Develog

코드스테이츠 64일차 본문

코드스테이츠

코드스테이츠 64일차

안형준 2022. 7. 26. 18:00

Spring Security의 인가 처리 흐름

1. 사용자가 HTTP 요청을 한다.

2. FilterSecurityInterceptor는 SecurityContextHolder로부터 Authentication을 얻는다.

3. FilterSecurityInterceptor는 FilterInvocation을 생성한다,

4. FilterInvocation을 SecurityMetadataSource에 전달하고 ConfigAttribute를 생성합니다. ConfigAttribute는 다수가 존재할 수 있다.

5. Authentication, ConfigAttribute를 AccessDecisionManager로 전달한다.

6. AccessDecisionManager는 decide(Authentication, Collection) 메소드를 호출하여 AccessDecisionVoter에 처리를 위임합니다. AccessDecisionVoter 또한 여러개일 수 있다.

7. 각 AccessDecisionVoter는 vote() 메소드를 호출하여 해당 Authentication의 Authorities 와 ConfigAttribute 목록들을 비교하여 접근 권한을 부여하거나 제한한다.
7-1 권한이 존재하는 경우 AccessDecisionManager에 ACCESS_GRANTED를 전달한다.
7-2 권한이 존재하지 않는 경우 AccessDeniedException 예외를 발생시킨다.

 

Authorization Architecture

Authorization은 특정 요청의 주체인 클라이언트의 접근 권한을 검증하는데 필요로 한다.

Spring Security는 인증 방법에 관계없이 애플리케이션에 일관적이고 간단하게 인증 서비스를 적용할 수 있도록한다.

 

Authorities

GrantedAuthority 객체는 principal에게 부여한 권한을 나타낸다.

  • GrantedAuthority는 메서드가 1개만 있는 인터페이스이다.
  • String getAuthority();

 

인증

  • AuthenticationManager가 Authentication 객체에 설정해준다.

 

인가

  • AccessDecisionManager가 해당 Authentication 객체에서 GrantedAuthority를 꺼내서 접근 권한을 결정한다.
  • String으로 쉽게 GrantedAuthority를 조회한다.
  • 복잡한 케이스로 간주하는 경우엔 getAuthority()에선 null을 리턴한다.
  • ㄴex) 고객 계정 번호에 따라 적용할 작업과 권한 임계치 리스트를 저장하는 일 등이 있을 수 있다.
  • ㄴnull을 리턴했다는 것은 복잡한 GrantedAuthority를 의미하고 AccessDecisionManager에 구체적인 코드가 있어야 한다는 뜻이다.

 

한 가지 GrantedAuthority 구현체SimpleGrantedAuthority를 제공한다.

  • 사용자가 지정한 String을 GrantedAuthority로 변환해준다.
  • 시큐리티 아키텍처에 속하는 모든 AuthenticationProvider는 Authenticaion 객체에 값을 채울 때 SimpleGrantedAuthority를 사용한다.

 

SimpleGrantedAuthority

SimpleGrantedAuthority 클래스는 String 형태의 사용자의 권한에 해당하는 문자열을 GrantedAuthority 로 반환해준다.

 

Pre-Invocation Handling

스프링 시큐리티는 method invocation이나 웹 요청 같은 보안 객체에 대한 접근을 제어하는 인터셉터를 제공한다.

호출을 허용할지 말지를 결정하는 pre-invocation 결정은 AccessDecisionManager에서 결정한다.

 

AccessDecisionManager

AbstractSecurityInterceptor에서 호출되며 최종적인 접근 제어를 결정한다.

void decide(Authentication authentication, Object secureObject,

    Collection<ConfigAttribute> attrs) throws AccessDeniedException;



boolean supports(ConfigAttribute attribute);



boolean supports(Class clazz);

decide

  • 권한 부여하기 위한 결정을 내리는 데 필요한 모든 정보가 메서드에 전달된다.
  • securie Object를 전달하면 실제 보안 객체를 실행할 때 인자를 검사할 수 있다.

supports(ConfigAttribute)

  • 기동 시점에 AbstractSecurityInterceptor가 호출하며 AccessDecisionManager가 전달된 ConfigAttribute의 처리 가능 여부를 결정한다.

supports(Class)

  • 시큐리티 인터셉터 구현체가 호출하며 설정해둔 AccessDecisionManager에서 시큐리티 인터셉터가 제출할 보안 객체 타입을 지원하는지 확인한다.

 

After Invocation Handling

보안 객체를 실행하기 전에는 AbstractSecurityInterceptor가 AccessDecisionManager를 호출한다.

하지만 실제로 보안 객체가 리턴하는 객체를 바꿔야 하는 애플리케이션도 있다.

(직접 AOP 관심사를 구현해도 되지만 ACL 기능과 통합되는 몇 가지 구현체를 가진 훅도 제공된다.)

 

서블릿 아키텍처와 구현체를 기반으로 권한 부여

FilterSecurityInterceptor는 HttpServletRequest를 사용해서 권한을 인가하고.

인터셉터는 FilterChainProxy에 하나의 보안 필터로 추가된다.

1. FilterSecurityInterceptor가 SecurityContextHolder에서 Authenticaion 객체를 얻는다.

2. FilterSecurityInterceptor가 넘겨받은 HttpServletRequest, HttpServletResponse, FilterChain으로 FilterInvocation을 생성한다.

3. ConfigAttributes를 얻기 위해 FilterInvocation을 SecurityMetadataSource에 전달한다.

4. Authentication, FilterInvocation, ConfigAttribute를 AccessDecisionManager로 전달한다.

  • 인가 거절 → AccessDeniedException 예외를 던진다. (ExceptionTranslationFilter가 처리한다.)
  • 인가 승인 → FilterSecurityInterceptor는 일반적인 애플리케이션 프로세스를 실행할 수 있도록 FilterCahin을 이어간다.

 

스프링 시큐리티에서 권한을 인가하려면 기본적으로 모든 요청을 인증해야 한다.

http

// ...

.authorizeRequests(authorize -> authorize

.anyRequest().authenticated()

);

 

Config 설정으로 보는 인가

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)

public class SecurityConfig {



    @Bean

    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http.authorizeHttpRequests()

.antMatchers("/member/**").authenticated()

                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")

                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')");

return http.build();

}

}

만약 authorizeRequests를 위와 같이 설정할 경우 다음과 같다.

  • /member/** 요청의 경우 인증된 사용자들에게 접근이 허용된다.
  • /manager/** 요청은 ROLE_ADMIN 또는 ROLE_MANAGER의 Role을 가진 사용자에게만 접근이 허용된다.
  • /admin/** 요청은 ROLE_ADMIN 사용자만을 접근 허용한다.

 

표현식 기반 접근 제어

Expression-Based Access Control

스프링 시큐리티 3.0부터 간단한 설정 속성과 voter로 접근 권한을 결정하는 방법 외에도 스프링 EL 표현식을 사용해 인가 메커니즘을 구현할 수 있다.

표현식 기반으로 접근을 제어할 땐 동일한 아키텍처를 사용하지만, 복잡한 Boolean 로직을 간단한 표현식 하나로 캡슐화할 수 있다.

 

  • 스프링 시큐리티는 스프링 EL 표현식을 사용한다.
  • 표현식을 평가할 땐 평가 컨텍스트의 일부로 루트 객체를 사용한다.
  • 스프링 시큐리티는 웹과 메서드 시큐리티 전용 클래스를 루트 객체로 사용하기 때문에 별도의 내장 표현식을 사용 가능하며 principal 등에 접근할 수 있다.
표현식 설명
hasRole(Stirng role) - 현재 보안 주체(principal) 지정된 역할을 갖고 있는지 여부를 확인하고 가지고 있다면 true 리턴한다.
- hasRole(’admin’)처럼 파라미터로 넘긴 role ROLE_ 시작하지 않으면 기본적으로 추가한다.
(DefaultWebSecurityExpressionHandler defaultRolePrefix 수정하면 커스텀할 있다.)
hasAnyRole(String… roles) - 현재 보안 주체가 지정한 역할 1개라도 가지고 있으면 true 리턴한다.
(문자열 리스트를 콤마로 구분해서 전달한다.)
- ex) hasAnyRole(’admin’, ‘user’)
hasAuthority(String authority) - 현재 보안 주체가 지정한 권한을 갖고 있는지 여부를 확인하고 가지고 있다면 true 리턴한다.
- ex) hasAuthority(’read’)
hasAnyAuthority(String… authorities) - 현재 보안 주체가 지정한 권한 하나라도 있으면 true 리턴한다.
- ex) hasAnyAuthority(’read’, ‘write’)
principal - 현재 사용자를 나타내는 principal 객체에 직접 접근할 있다.
authentication - SecurityContext 조회할 있는 현재 Authentication 객체에 직접 접근할 있다.
permitAll - 항상 true 평가한다.
denyAll - 항상 false 평가한다.
isAnonymous() - 현재 보안 주체가 익명 사용자면 true 리턴한다.
isRememberMe() - 현재 보안 주체가 remember-me 사용자면 true 리턴한다.
isAuthenticated() - 사용자가 익명이 아닌 경우 true 리턴한다.
isFullyAuthenticated() - 사용자가 익명 사용자나 remember-me 사용자가 아니면 true 리턴한다.
hasPermission(Object target, Object permission) - 사용자가 target 해당 permission 권한이 있으면 true 리턴한다.
-ex) hasPermission(domainObject, ‘read’)
hasPermission(Object targetId, String targetType, Object permission) - 사용자가 target 해당 permission 권한이 있으면 true 리턴한다.
- ex) hasPermission(1, ‘com.example.domain.Message’, ‘read’)

 

메서드 보안 표현식

메서드 시큐리티는 단순한 허가 또는 거절보다 조금 더 복잡한 규칙을 사용한다.

스프링 시큐리티 3.0은 표현식을 종합적으로 지원하기 위한 새로운 애노테이션을 도입했다.

 

@Pre and @Post Annotations

네 가지 애너테이션이 표현식 속성을 지원한다.

  • @PreAuthorize
  • ㄴ메서드를 실행 여부와 관련된 결정들을 한다.
  • ㄴex) hasRole, hasPermission 등등
  • @PreFilter
  • @PostAuthorize
  • @PostFilter

사전/사후 권한 체크를 지원하고 제출한 컬렉션 인자나 리턴한 값을 필터링 할 수 있다.

'코드스테이츠' 카테고리의 다른 글

코드스테이츠 67-68일차  (0) 2022.07.29
코드스테이츠 65-66일차  (0) 2022.07.27
코드스테이츠 63일차  (0) 2022.07.26
코드스테이츠 60-61일차  (0) 2022.07.21
코드스테이츠 59일차  (0) 2022.07.19