SW 개발/Spring

Spring Boot, Spring Security, 사용자 역할, 권한 인증하기

지단로보트 2019. 7. 5. 19:01

목표 및 기대효과

  • Spring Boot 기반 프로젝트에 Spring Security를 이용하여 시스템에 대한 사용자 접근 제어를 손쉽게 구현할 수 있다.

사용자

  • 사용자(UserDetails)는 시스템을 이용하는 사용자를 의미한다. Spring Security는 사용자를 대표하는 org.springframework.security.core.userdetails.UserDetails 인터페이스, 사용자 인증 관련 기능을 제공하는 org.springframework.security.core.userdetails.UserDetailsService 인터페이스를 제공한다. 시스템마다 사용자를 이루는 요소와 방법이 다양하기 때문에 인터페이스로 추상화하고 각자 입맛에 맞게 구현체를 개발하도록 설계되어 있다.

역할(Role)과 권한(Permission)

  • 역할(Role)은 사용자에게 부여된 역할을, 권한(Permission = Authority)은 사용자의 역할에 부여된 권한을 의미한다. 개념적으로 1개의 역할은 복수개의 권한을 가질 수 있다. (즉, 1:N의 관계이다.)
  • Spring Security는 역할과 권한을 구조적으로 분리하지 않고, UserDetails#getAuthorities()로 획득 가능한 GrantedAuthority 인터페이스 구현체의 목록으로 설계하였다. 다만, 역할의 경우 문자열 앞에 ROLE_을 붙이도록 하여, 권한과 구분되도록 설계하였다. 개발자는 실제 구현시 역할과 권한을 별도의 저장소로 분리하여 1:N의 관계로 설계하고, UserDetails 구현체 작성시에는 해당 사용자에게 부여된 역할과 권한을 모두 문자열로 UserDetails#getAuthorities()을 통해 획득되도록 구현하면 된다. [관련 링크1] [관련 링크2]
  • 역할은 스프링 빈의 메써드 레벨에서 @PreAuthorize(“hasRole(‘ADMIN’)”)와 같이 특정 역할을 부여 받았는지 검사할 수 있다. 물리적으로는 ROLE_ADMIN이라는 문자열을 검사하게 된다.
  • 권한은 스프링 빈의 메써드 레벨에서 @PreAuthorize(“hasAuthority(‘RESET_USER_PASSWORD’)”)와 같이 특정 권한을 부여 받았는지 검사할 수 있다. 물리적으로는 RESET_USER_PASSWORD라는 문자열을 검사하게 된다.

build.gradle 종속성 추가

  • Spring Boot 기반 프로젝트는 /build.gradle 파일에 아래 내용을 추가하면 Spring Security 사용 준비가 완료된다.
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-security")
}

SecurityConfig 작성

package com.jsonobject.example.demo

import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig : WebSecurityConfigurerAdapter() {

    fun configGlobal(auth: AuthenticationManagerBuilder) {

        // 테스트 용도의 인메모리에 저장되는 사용자 계정과 역할, 권한 정보를 생성한다.
        // 프로덕션 레벨에서는 물리 저장소에 CRUD를 수행하는 UserDetails, UserDetailsService 구현체를 작성해야 한다.
        auth.inMemoryAuthentication().withUser("user").password("password").roles("ADMIN").authorities("GET_USER_INFO")
    }
}

@PreAuthorize, 역할과 권한 인증

  • Spring Security 생태계에서는 @PreAuthorize를 이용하여 스프링 빈의 메써드 레벨에서 역할과 권한을 제어할 수 있다. 그렇다면 실제 지정된 역할과 권한을 충족하지 않는 사용자의 접근 시도시 오류 제어는 어떻게 해야 할까? 오류 발생시 org.springframework.security.access.AccessDeniedException 예외를 발생시키는데, Spring Security에서의 오류 제어는 사용자 인증 오류(403 FORBIDDEN)의 경우 AccessDeniedHandler 구현체를 작성하면 되고, 역할과 권한 인증 오류(401 UNAUTHORIZED)의 경우 AuthenticationEntryPoint 구현체를 작성하면 된다. [관련 링크]

참고 글