0. 시작하게 된 계기 및 다짐 😮
이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프
에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.
1. 학습 목표 😮
목표 | 결과 |
---|---|
Spring Security 인가 처리 과정 이해 | O |
Spring Security의 인가 처리과정 학습 | O |
Spring Rest Docs를 위한 기본 설정 및 사용 방법을 이해 | O |
Spring Rest Docs를 사용해서 API 문서를 생성 및 배포 | O |
2. 정리 😮
Spring Security의 인가 처리 흐름
0. Authorization - 인가
1. Overview
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 예외를 발생시킵니다.
2. FilterSecurityInterceptor
3. FilterInvocation
4. SecurityMetadataSource
5. AccessDecisionManager
인터페이스로 인증,요청,권한 정보를 이용하여 사용자의 자원 접근/허용 거부의 최종 주체
여러 개의 Voter(AccessDecisionVoter)을 가질 수 있고 이를 통해 각각 접근 허용/거부등의 값을 리턴받음
decide()메소드를 호출하여 AccessDecisionVoter에 처리를 위임한다. 여러개 일 수 있음
AccessDecisionVoter는 vote() 메소드를 통해 Authentication의 Authorities, FilterInvocation, ConfigAttribute 목록들을 비교하여 접근 권한을 부여하거나 제한
1). AffirmativeBased : Voter들 중 하나라도 통과하면 허가 , 'Or'
2). ConsensusBased : Voter들의 통과 개수에 따라 다수결로 결정
3). UnanimousBased : 'And'와 같음
6. AccessDecisionVoter
Authorization Architecture
1. Overview
1). 사용자가 자원 접근(Request)
2). FilterSecurityInterceptor에서 요청을 받아서 인증여부를 확인한다.
- 인증객체를 가지고 있는지 확인한다.
- 인증객체가 없으면(null) AuthenticationException 발생
- ExceptionTranslationFilter에서 해당 예외를 받아서 다시 로그인 페이지로 이동하던가 후처리를 해준다.
3). 인증객체가 있을 경우 SecurityMetadataSource는 자원에 접근하기 위해 설정된 권한정보를 조회해서 전달해준다.
- 권한 정보를 조회한다.
- 권한정보가 없으면(null) 권한 심사를 하지 않고 자원 접근을 허용한다.
4). 권한 정보가 있을 경우 AccessDecisionManager 에게 권한 정보를 전달하여 위임한다.
- AccessDecisionManager는 최종 심의 결정자다.
5). AccessDecisionManager가 내부적으로 AccessDecisionVoter(심의자)를 통해서 심의 요청을 한다.
6). 반환된 승인/거부 결과를 가지고 사용자가 해당 자원에 접근이 가능한지 판단한다.
- 접근이 거부되었을 경우 AccessDeniedException이 발생한다.
- ExceptionTranslationFilter에서 해당 예외를 받아서 다시 로그인 페이지로 이동하던가 후처리를 해준다.
7). 접근이 승인되었을 경우 자원 접근이 허용된다.
2. Authorities
Authentication에서는 모든 '인증'구현체가 각 GrantedAuthority 객체 리스트로 받은 권한들
1). GrantedAuthority
2). 인증
3). 인가
3. Pre-Invocation Handling
'웹 요청 같은 보안 객체'에 대한 접근을 제어하는 인터셉터 제공
허용할지 말지는 결정하는 pre-invocation 결정은 AccessDecisionManager에서 결정
4. AccessDecisionManager
AbstractSecurityIneterceptor(FilterSecurityInterceptor 의 부모클래스)에서 호출되며, 최종적 접근 제어 결정
1). decide 메소드
2). supports(ConfigAttribute)
3). supports(class)
4). 그 외 정보
5. After Invocation Handling
1). 실제로 보안 객체가 리턴하는 객체를 바꿔야 하는 애플리케이션
- 직접 AOP 관심사를 구현해도 되지만, ACL 기능과 통합되는 몇 가지 구현체를 가진 훅도 제공
2). 그 외
- After Invocation Implementations
- Hierarchical Roles
2. 서블릿 아키텍처와 구현체를 기반으로 권한 부여
사진
OverView
0). 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을 이어간다.
2. 스프링 시큐리티에서 권한을 인가하려면, 기본적으로 모든 요청을 인증해야 한다.
[예제 Code]
http // ... .authorizeRequests(authorize -> authorize .anyRequest().authenticated() );
3. 표현식 기반 접근 제어
스프링 EL 표현식 사용 [ 동작방식 학습]
표현식을 평가할 땐 평가 컨텍스트의 일부로 루트 객체를 사용
스프링 시큐리티는 웹과 메서드 시큐리티 전용 클래스를 루트 객체로 사용하기 때문에 별도의 내장 표현식을 사용 가능하며 principal 등에 접근할 수 있다.
https://docs.spring.io/spring-security/reference/servlet/authorization/expression-based.html 참조
표현식을 종합적으로 지원하기 위한 새로운 애너테이션 도입
사전/사후 권한 체크를 지원하고 제출한 컬렉션 인자나 리턴한 값을 필터링 할 수 있음
1). @Pre
2). @Post
3). @PreAuthorize
[예제Code]
@PreAuthorize("#contact.name == authentication.name") public void doSomething(Contact contact);
4). @PostAuthorize
- 메소드가 실행 된 이후에 실행됨
- 메소드가 실행 된 이후의 return 값을 활용할 수 있음
- returnObject 예약어로 리턴 객체에 접근할 수 있음
[예제Code]
@PostAuthorize("isAuthenticated() and (( returnObject.name == principal.name ) or hasRole('ROLE_ADMIN'))") @RequestMapping( value = "/{seq}", method = RequestMethod.GET ) public User getuser( @PathVariable("seq") long seq ){ return userService.findOne(seq); }
5). @PreFilter
6). @PostFilter
3. 피드백 😮
4. 앞으로 해야 될 것 😮