[ 정수원 스프링 시큐리티 #1 ] 스프링 시큐리티 기본 API & Filter 이해 (6)

김수호·2024년 3월 9일
0
post-thumbnail

지난 포스팅에 이어, 이번 포스팅에서는 11) 의 내용을 정리한다.

👉 목차는 다음과 같다.

1) 인증 API - 프로젝트 구성 및 의존성 추가
2) 인증 API - 사용자 정의 보안 기능 구현
3) 인증 API - Form Login 인증
4) 인증 API - Form Login 인증 필터 : UsernamePasswordAuthenticationFilter
5) 인증 API - Logout 처리, LogoutFilter
6) 인증 API - Remember Me 인증
7) 인증 API - Remember Me 인증 필터 : RememberMeAuthenticationFilter
8) 인증 API - 익명사용자 인증 필터 : AnonymousAuthenticationFilter
9) 인증 API - 동시 세션 제어, 세션 고정 보호, 세션 정책
10) 인증 API - 세션 제어 필터 : SessionManagementFilter, ConcurrentSessionFilter
11) 인가 API - 권한 설정 및 표현식
12) 인증/인가 API - 예외 처리 및 요청 캐시 필터 : ExceptionTranslationFilter, RequestCacheAwareFilter
13) Form 인증 - 사이트 간 요청 위조 : CSRF, CsrfFilter

바로 하나씩 확인해보자.


11) 인가 API - 권한 설정 및 표현식

이전 내용까지는 주로 인증 API와 관련된 내용을 학습했다.
이번 내용에서는 인가 API는 어떻게 설정하고, 어떻게 동작하는지 알아보자.
(권한 설정과 표현식을 통해서 인가 처리)

✔️ 권한 설정

  • 스프링 시큐리티가 지원하는 권한 설정은 선언적 방식과 동적 방식으로 나눠볼 수 있다.
  • 1) 선언적 방식
    • URL 방식
      • http.antMatchers("/users/**").hasRole("USER")
    • Method 방식
      • @PreAuthorize("hasRole('USER')") public void user() { System.out.println("user"); }
  • 2) 동적 방식 - DB 연동 프로그래밍
    • URL 방식
    • Method 방식

이번 내용에서는 [선언적 방식 - URL 방식] 으로 권한을 설정하는 내용에 대해서 알아보고,
Method 방식과, 동적 방식은 이후 실전 프로젝트 인가 편에서 살펴보자.

 

URL 방식

  • 참고)
    • 인가 설정도, 인증 설정을 했던 configure(HttpSecurity http) 메서드 안에서 설정하면 된다.
    • http.antMatcher("경로") : 먼저, antMatcher 를 사용해서 특정 경로를 지정할 수 있다. 그러면 현재 사용자의 요청이 해당 경로로 요청될 때만 설정 클래스에 작성한 보안 기능이 작동하게 된다. 만약 해당 경로가 아닌, 다른 경로로 요청하게 되면, 설정 클래스의 보안 기능은 작동하지 않는다. 따라서, 특정한 자원 경로로 제한해서 인증과 인가 처리를 하고자 할때, antMatcher("경로") 로 적용해주면 된다. 참고로 만약 위 예시에서 antMatcher("경로")를 생략하게 되면, 특정한 자원 경로가 아닌, 모든 요청 경로에 대해서 보안 검사를 진행하게 된다.
    • authorizeRequests() : 요청에 대한 인가 설정을 시작한다. chaining 형태로 설정할 수 있다.
    • anyMatchers("경로1", "경로2", ..) : anyMatchers 안에는 여러개의 경로를 작성할 수 있다. 그러면 클라이언트의 request에 담긴 요청 경로와 작성된 경로들을 매칭하고, 동일한 경로 정보가 있거나 포함 관계에 있으면, 그 뒤에 작성한 권한 정보(ex. .permitAll(), .hasRole(..), ..)를 통해서 인가 심사를 한다. 참고로 만약 매칭되지 않으면, 그 다음 anyMatchers(..) 로 계속적으로 이동하여 인가 처리와 관련된 심사를 진행한다. 따라서 작성된 모든 인가 심사를 통과해야 자원에 접근된다.
    • anyRequest().authenticated() : 특정한 자원 경로에 대한 권한 정보를 제외하고, 그 밖의 모든 요청에 대해서는 인증을 받아야 접근이 가능하다는 의미이다.
    • (주의 사항) 설정 시 구체적인 경로가 먼저 오고 그것 보다 큰 범위의 경로가 뒤에 오도록 해야 한다.
      • 스프링 시큐리티는 우리가 설정한 각각의 설정을 위에서 부터 아래로 순서대로 해석을 해서 인가 처리를 진행한다.

 

✔️ 표현식

  • 참고)
    • 스프링 시큐리티는 인증과 관련된 표현식과 권한과 관련된 표현식을 제공한다.
    • 각각의 표현식 앞에 자원들을 설정한다.
      • ex) antMatchers("경로1", "경로2").permitAll()
    • 참고) 인증된 사용자는 anonymous() 표현식에 설정된 자원에 접근할 수 없다. (오로지 익명 사용자만 접근 가능하다.)
      • anonymous() 는 ROLE_ANONYMOUS 권한을 가진 사용자만 접근할 수 있다. 인증된 ROLE_USER 권한을 가진 사용자는 접근할 수 없다. 그러므로, 인증된 사용자나 익명 사용자가 모두 접근 가능해야 하는 경우 permitAll() 을 사용해야 한다.
    • 참고) hasRole(String) 작성시, ROLE_USER 라 할때, ROLE_ prefix는 붙이지 않아야 한다. 붙이면 오류가 발생한다.
      • ex) antMatchers("/users/**").hasRole("USER")
    • 참고) hasAuthority(String) 작성시, ROLE_USER 라 할때, ROLE_ prefix를 붙여야 한다.
      • ex) antMatchers("/goods/**").hasAuthority("ROLE_USER")

 

👉 이제 사용자를 생성하고, 각 사용자에게 권한을 부여하자. 그리고 인가 API를 통해서 자원과 권한 정보를 설정하고 정상적으로 인가 처리가 동작하는지 테스트해보자.

  • SecurityConfig 수정
    • 참고) 여러가지 방식으로 사용자를 생성할 수 있지만, 이번 내용에서는 간단히 설정 클래스 안에서 AuthenticationManagerBuilder 를 통해 메모리 기반으로 사용자를 생성하고, 권한을 부여했다.
    • 참고) 인가 정책은,
      • 1) /user 경로는 USER 권한을 가진 사용자만 접근 가능하고,
      • 2) /admin/pay 경로는 ADMIN 권한을 가진 사용자만 접근가능하다. 그리고
      • 3) /admin/** 하위 모든 경로는 ADMIN 권한 또는 SYS 권한을 가진 사용자만 접근이 가능하고,
      • 4) 그 외 모든 경로는 권한 상관없이 인증을 받은 사용자는 모두 접근이 가능하도록 인가 처리를 설정했다.
  • SecurityController 수정: 컨트롤러에 각 경로에 해당하는 것을 추가하자.
  • 이제 서버를 기동해서 각 사용자가 해당 자원에 접근했을 때, 어떻게 인가 처리가 이뤄지는지 살펴보자. 서버 기동 후 루트 경로로 접근해보자.
    • 그러면 anyRequest().authenticated() 에 의해, 인증된 사용자가 아니므로 로그인 페이지로 이동한다.
  • user 계정으로 로그인해보자.
    • 정상적으로 루트 경로로 접근되는 것을 확인할 수 있다.
  • /user 경로로 접근해보자.
    • 시큐리티 설정에 의해 /user 경로에 접근하기 위해서는 USER 권한이 필요하다. 해당 경로로 요청한 user 계정은 USER 권한을 가지고 있으므로 정상적으로 접근되는 것을 확인할 수 있다.
  • 이제 /admin 으로 접근해보자.
    • 해당 경로에 접근하기 위해서는 ADMIN 또는 SYS 권한이 있어야 하지만, 현재 user 계정은 해당 권한이 아니므로, 해당 자원에 접근할 수 없다. (Forbidden 403 오류로 오류페이지가 노출되는 것을 확인할 수 있다.)
    • 참고) 마찬가지로 /admin/pay 경로도 접근할 수 없다.
  • 로그아웃 후, 이번엔 sys 계정으로 로그인해보자. 그리고 /admin 경로를 요청해보자.
    • /admin 경로에 접근하기 위해서는 ADMIN 또는 SYS 권한이 있어야 한다. sys 계정은 SYS 권한을 가지고 있으므로 접근 가능하다.
  • 이번엔 /admin/pay 경로로 접근해보자.
    • /admin/pay 경로에 접근하기 위해서는 ADMIN 권한이 있어야 한다. sys 계정은 SYS 권한을 가지고 있으므로 접근이 안되는 것을 확인할 수 있다.
    • 참고) 마찬가지로 /user 경로에 접근해도, USER 권한이 아니므로 접근할 수 없다.
    • 참고) 하나의 계정으로 여러 권한에 해당하는 경로로 접근하기 위해서, 권한을 여러개 할당할 수 있다.
      • ex) auth.inMemoryAuthentication().withUser("admin").password("{noop}1111").roles("ADMIN", "SYS", "USER");
      • 이후 rolehierarchy 기능을 통해 권한 계층을 적용하면 이렇게 해줄 필요는 없다. 참고로만 알아두자.
    • 참고) 만약 .antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')").antMatchers("/admin/pay").hasRole("ADMIN") 의 위치를 바꾸게 된다면, SYS 권한을 가진 sys 계정이 /admin/pay 경로에 접근 가능하게 된다.
      • 설정 시 구체적인 경로가 위에 오도록 먼저 작성해줘야 함에 주의하자.

강의를 듣고 정리한 글입니다. 코드와 그림 등의 출처는 정수원 강사님께 있습니다.

profile
현실에서 한 발자국

0개의 댓글