인가 프로세스

enxnong·2024년 7월 5일
0

스프링 시큐리티

목록 보기
7/13

정수원님의 강의 스프링 시큐리티 완전 정복 [6.x 개정판] 보면서 공부한 내용입니다.

요청 기반 권한 부여

HttpSecurity.authorizeHttpRequests()

  • 클라이언트의 요청(브라우저를 통해 URL로 서버에 요청하는 것)
    즉, HttpServletRequest 에 대한 권한 부여를 모델링 하는 것이며 이를 위해 HttpSecurity 인스턴스를 사용하여 권한 규칙을 선언 할 수 있다
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
  http.authorizeHttpRequests(authorize -> 
    // 요청 객체에 대하여 권한을 모델링 하겠다라는 의미
    authorize.anyRequest().authenticated()); 
	// 어떤 요청에도 인증이 되어야한다는 권한 설정
    // 즉, 애플리케이션의 모든 엔드포인트가 최소한 인증된 보안 컨텍스트가 있어야 한다고 알리는 것
  return http.build();
}

authorizeHttpRequests() API

requestMatchers()

  • 해당 메소드는 HTTP 요청의 URL 패턴, HTTP 메소드, 요청 파라미터 등을 기반으로 어떤 요청에 대해서는 특정 보안 설정을 적용하고 다른 요청에 대해서는 적용하지 않도록 세밀하게 제어할 수 있게 해 준다
  • 애플리케이션의 보안 요구 사항에 맞춰서 유연한 보안 정책을 구성할 수 있다
    • ex) 특정 API 경로에만 CSRF 보호를 적용하거나 특정 경로에 대해 인증을 요구하지 않도록 설정 가능
  • requestMatchers(String... urlPatterns)
    • 보호가 필요한 자원 경로를 한 개 이상 정의
  • requestMatchers(RequestMatcher... requestMatchers)
    • 보호가 필요한 자원 경로를 한 개 이상 정의
    • AntPathRequestMatcher, MvcRequestMatcher 등의 구현체를 사용 가능
  • requestMatchers(HttpMethod method, String... utlPatterns)
    • Http Method 와 보호가 필요한 자원 경로를 한 개 이상 정의

→ requestMatchers() 안에 들어가는 내용은 "서버가 보호해야되는 자원으로 임의대로 접근할 수 없고 접근하기 위해서는 특정 권한 규칙을 따라야한다"라고 하는 url 또는 패턴들을 설정]

  • 엔드 포인트 & 권한 부여
    • 엔드포인트 : requestMatchers("/admin")
    • 권한 부여 : hasRole("ADMIN")
    • ex) requestMatchers("/admin").hasRole("ADMIN")

💡 주의사항

  • 클라이언트 요청에 대하여 위에서 부터 아래로 나열된 순서대로 처리하며 요청에 대하여 첫 번째 일치만 적용되고 다음 순서로 넘어가지 않는다
  • 즉, /admin/가 /admin/db 요청을 포함하므로 /admin/를 먼저 정의하면 /admin/db의 권한 규칙이 적용되지 않을 수 있다.
  • 그러므로 엔드 포인트 설정 시 좁은 범위(/admin/db)의 권한 규칙을 먼저 설정한 후 큰 번위(/admin/**)를 설정해야 한다
  • ROLE에 따른 접근

    • ROLE : user 접근 허용인 페이지
    • ROLE : user 접근 불가능 페이지

HttpSecurity.securityMatcher()

  • 특정 패턴에 해당하는 요청에만 보안 규칙을 적용
  • 패턴 설정 : http.securityMatcher("/api/**").authorizeHttpRequests(auth -> auth.requestMatchers(…))
    • HttpSecurity 를 /api/로 시작하는 URL에만 적용하도록 구성
  • securityMatcher(String... urlPatterns)
    • 특정 자원 보호가 필요한 경로를 정의한다
  • securityMatcher(RequestMatcher... requestMatchers)
    • 특정 자원 보호가 필요한 경로를 정의한다. AntPathRequestMatcher, MvcRequestMatcher 등의 구현체를 사용할 수 있다

표현식 및 커스텀 권한 구현

  • 표현식을 사용해서 권한 규칙을 설정하도록 WebExpressionAuthorizationManager 를 제공
  • 표현식은 시큐리티가 제공하는 권한 규칙을 사용하거나 사용자가 표현식을 커스텀하게 구현해서 설정 가능
  • 사용방법 : requestMatchers().access(new WebExpressionAuthorizationManager("expression"))
requestMatchers("/admin/db").access(anyOf(hasAuthority("db"), hasRole("ADMIN")))

메서드 기반 권한 부여

  • 메서드 기반 권한 부여를 활성화하기 위해서는 설정 클래스에 @EnableMethodSecurity 어노테이션을 추가해야 한다

@PreAuthorize

  • 메소드가 실행되기 전에 특정한 보안 조건이 충족되는지 확인하는 데 사용되며 보통 서비스 또는 컨트롤러 레이어의 메소드에 적용되어 해당 메소드가 호출되기 전에 사용자의 인증 정보와 권한을 검사한다
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public void adminOnlyMethod() {
	// 관리자 역할을 가진 사용자만 실행할 수 있는 메소드
}

@PostAuthorize

  • 메소드가 실행된 후에 보안 검사를 수행하는 데 사용한다
  • 즉, 메소드 실행 후 결과에 대한 보안 조건을 검사하여 특정 조건을 만족하는 경우에만 사용자가 결과를 받을 수 있도록 한다
@PostAuthorize("returnObject.owner == authentication.name")
public BankAccount getAccount(Long id) {
	// returnObject : return하는 객체 (BankAccount)
    // returnObject.owner : return하는 객체안에 있는 속성
	// => 계정를 반환하지만 계정의 소유자만 결과를 볼 수 있음
 return new BankAccount();
}

@PreFilter

  • 메소드가 실행되기 전에 메소드에 전달된 컬렉션 타입의 파라미터에 대한 필터링을 수행하는데 사용된다
  • 주로 사용자가 보내온 컬렉션(배열, 리스트, 맵, 스트림) 내의 객체들을 특정 기준에 따라 필터링하고 그 중 보안 조건을 만족하는 객체들에 대해서만 메소드가 처리하도록 할 때 사용된다
@PreFilter("filterObject.owner == authentication.name")
public Collection<BankAccount> updateAccounts(BankAccount[] data){
	return data;
    // BankAccount에 저장되어 있는 여러개의 객체들을 PreFilter 조건에 따라 필터링해서 반환해줌
    // BankAccount에 a,b,c 객체가 있는데 필터링된 것이 b에만 맞는다면 b만 return된다
}

@PostFilter

  • 메소드가 컬렉션을 반환할 때 반환되는 각 객체가 특정 보안 조건을 충족하는지 확인하고 조건을 만족하지 않는 객체들을 결과에서 제거한다

💡 PreFilter와 반대

  • PreFilter는 미리 필터링해서 그 결과를 가지고 메서드 안으로 진입해서 처리한다면, PostFilter는 어떤 결과가 주어지지 않지만 메서드에 진입한 뒤 결과들을 가지고 와서 다시 반환할 때 특정 조건에 만족하는 객체만 반환한다
@PostFilter("filterObject.owner == authentication.name")
public List<BankAccount> readAccounts1(){ // 특정 조건을 받지 않는다
	return dataService.readList();
    // PostFilter 필터에 만족하는 객체만 반환
}

@Secured

  • 메소드에 적용하면 지정된 권한(역할)을 가진 사용자만 해당 메소드를 호출할 수 있으며 더 풍부한 형식을 지원하는 @PreAuthorize 사용을 권장한다
  • 해당 어노테이션을 사용하려면 스프링 시큐리티 설정에서 @EnableMethodSecurity(securedEnabled = true) 설정을 활성화해야 한다
@Secured("ROLE_USER")
public void performUserOperation() {
	// ROLE_USER 권한을 가진 사용자만 이 메소드를 실행할 수 있다.
}

JSR-250

  • @RolesAllowed, @PermitAll 및 @DenyAll 어노테이션 보안 기능이 활성화 된다
  • 해당 어노테이션을 사용하려면 스프링 시큐리티 설정에서 @EnableMethodSecurity(jsr250Enabled = true) 설정을 활성화해야 한다
  • @RolesAllowed
    • 권한을 가진 사용자만 접근 가능
    • ex) @RolesAllowed("USER")
  • @PermitAll
    • 모든 사용가 접근 가능
  • @DenyAll
    • 모든 사용자 접근 불가능

메타 주석 사용

  • 메서드 보안은 애플리케이션의 특정 사용을 위해 편리성과 가독성을 높일 수 있는 메타 주석을 지원한다
// IsAdmin이라는 어노테이션 설정
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ADMIN')")
public @interface IsAdmin {}

// @PreAuthorize("hasRole('ADMIN')")를 다음과 같이 @IsAdmin으로 간소화할 수 있다
@IsAdmin 
public BankAccount readAccount(Long id) {
	// ADMIN 권한을 가진 사용자에게 메소드 호출이 승인 될 수 있다
}

클래스 레벨 권한 부여

  • 메서드에 어노테이션을 선언한 메소드는 클래스 수준의 어노테이션을 덮어쓰게 된다
@Controller
@PreAuthorize("hasAuthority('ROLE_USER')") // 전체적으로는 실행되지만 클래스에 설정된 어노테이션이 있으면 클래스 어노테이션이 우선적으로 실행된다
public class MyController {
@GetMapping("/endpoint")
@PreAuthorize("hasAuthority('ROLE_ADMIN')") // 이 설정이 우선적으로 동작한다
public String endpoint() { ... }
}

정적 자원 관리

  • 스프링 시큐리티에서 정적 자원(이미지, CSS, JavaScript 파일 등)에 대한 요청이나 특정 엔드포인트가 보안 필터를 거치지 않도록 설정할 때 사용된다
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
 	return (webSecurity) -> {
 		webSecurity.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
        // 정적 자원들에 대해 보안 필터를 거치지 않도록 설정 가능
	 };
}

💡 StaticResourceLocation 클래스 안에 있는 정적 자원 경로

  • CSS("/css/**"),
  • JAVA_SCRIPT("/js/**"),
  • IMAGES("/images/**"),
  • WEB_JARS("/webjars/**"),
  • FAVICON("/favicon.", "//icon-*");
  • Ignoring 보다 permitAll 권장
    • 성능 제한
      • 이전 버전 : 모든 요청에 대해 세션을 확인했다. 필터에서 권한 검사를 할 때 반드시 세션에서 사용자의 인증 상태를 확인(무조건 Session 확인)해야 했다. 즉, Session으로부터 Security Context객체, 인증 객체 등을 가져오는 작업이 필요해서 permitAll을 하면 성능 제한이 발생할 수 있다
      • 스프링 시큐리티6 버전 : permitAll을 해도 사용자의 인증 상태를 확인하지 않기 때문에 성능 제한 이슈가 없어서 permitAll을 권장한다.
    • 안전한 헤더
      • JAVA_SCRIPT같은 경우는 XSS같은 해킹 공격에 취약하다. 그렇기에 permitAll을 통해서 스프링 시큐리티 보안 필터를 걸치게 되면 XSS 같은 해킹에 대해 차단할 수 있는 기능들을 추가할 수 있다.

💡 XSS란?

  • Cross Site Script의 약자로 웹사이트에 악성코드를 주입하는 행동을 말하며, 공격자가 웹사이트를 넘어서 공격한다는 뜻에서 유래되었다. 공격자는 웹사이트 입력 또는 출력 부분에 스크립트를 심어 웹사이트 뿐만 아니라 다른 사용자, 심지어 서버도 공격 가능하다는 특징이 있다. XSS는 대표적으로 Reflected XSS, Stored XSS, Dom based XSS 세 가지 유형으로 나뉜다.

계층적 권한

RoleHirerachy

  • 역할 간의 계층 구조를 설정하고 이를 기반으로 사용자에 대한 엑세스 규칙을 정의할 수 있다
<propertyname="hierarchy">
<value>
  ROLE_A>ROLE_B
  ROLE_B>ROLE_C
  ROLE_C>ROLE_D
  
// ROLE_A 를 가진 모든 사용자는 ROLE_B, ROLE_C 및 ROLE_D 도 가지게 된다
// ROLE_B는 ROLE_C, ROLE_D 권한을 가지게 된다
</value>
</property>
profile
높은 곳을 향해서

0개의 댓글