2025-04-08T19:56:24
This commit is contained in:
111
docs/security/04_역할과 권한.md
Normal file
111
docs/security/04_역할과 권한.md
Normal file
@@ -0,0 +1,111 @@
|
||||
아래는 "스프링 시큐리티" 책의 5장에 포함될 "역할(Role)과 권한(Authority)의 차이"와 "URL 기반 접근 제어"에 대한 내용입니다. 개념을 명확히 설명하고 실습 가능한 예제를 포함해 실무에서의 활용성을 높였습니다.
|
||||
|
||||
---
|
||||
|
||||
### 5장. 인가와 권한 관리
|
||||
|
||||
#### 5.1 역할(Role)과 권한(Authority)의 차이
|
||||
|
||||
스프링 시큐리티에서 **역할(Role)**과 **권한(Authority)**은 사용자가 시스템에서 무엇을 할 수 있는지를 정의하는 핵심 개념입니다. 두 용어는 종종 혼용되지만, 미묘한 차이가 있으며 이를 이해하면 인가를 더 세밀하게 관리할 수 있습니다.
|
||||
|
||||
##### 역할(Role)이란?
|
||||
역할은 사용자가 속한 그룹이나 직책을 나타내는 상위 수준의 개념입니다. 예를 들어, "관리자(ADMIN)", "일반 사용자(USER)", "게스트(GUEST)" 같은 이름으로 정의됩니다. 역할은 보통 사용자의 주요 책임이나 접근 범위를 나타내며, 스프링 시큐리티에서는 `ROLE_` 접두사를 붙여 표현합니다(예: `ROLE_ADMIN`).
|
||||
|
||||
역할은 단순하고 직관적이어서 소규모 애플리케이션이나 기본적인 권한 관리에 적합합니다. 예를 들어, 관리자 역할은 모든 기능을 사용할 수 있고, 일반 사용자는 제한된 기능만 접근할 수 있도록 설정할 수 있습니다.
|
||||
|
||||
##### 권한(Authority)이란?
|
||||
권한은 더 세분화된 접근 제어 단위로, 특정 작업이나 리소스에 대한 권한을 나타냅니다. 예를 들어, "게시글 작성(WRITE_POST)", "댓글 삭제(DELETE_COMMENT)", "사용자 관리(MANAGE_USERS)" 같은 구체적인 권한을 정의할 수 있습니다. 권한은 `ROLE_` 접두사 없이도 사용 가능하며, 역할보다 유연하게 설계할 수 있습니다.
|
||||
|
||||
권한은 역할에 포함될 수 있으며, 복잡한 시스템에서 세밀한 접근 제어를 구현할 때 유용합니다. 예를 들어, `ROLE_USER`는 `READ_POST`와 `WRITE_POST` 권한을 가질 수 있고, `ROLE_ADMIN`은 추가로 `MANAGE_USERS` 권한을 가질 수 있습니다.
|
||||
|
||||
##### 차이점과 활용
|
||||
- **수준**: 역할은 상위 수준(추상적), 권한은 하위 수준(구체적).
|
||||
- **표기**: 역할은 `ROLE_` 접두사를 기본으로 사용, 권한은 자유롭게 정의.
|
||||
- **복잡성**: 역할은 단순한 분류에 적합, 권한은 세부적인 제어에 유리.
|
||||
|
||||
스프링 시큐리티에서는 `GrantedAuthority` 인터페이스를 통해 두 개념을 통합적으로 관리하며, 설정에 따라 역할과 권한을 혼합해 사용할 수 있습니다. 예를 들어, `ROLE_ADMIN` 역할을 가진 사용자가 `MANAGE_USERS` 권한을 추가로 가질 수 있습니다.
|
||||
|
||||
#### 5.2 URL 기반 접근 제어
|
||||
|
||||
URL 기반 접근 제어는 스프링 시큐리티에서 가장 흔히 사용되는 인가 방식으로, 요청 URL 패턴에 따라 사용자의 접근을 허용하거나 차단합니다. 이를 통해 애플리케이션의 각 엔드포인트를 역할이나 권한에 따라 보호할 수 있습니다.
|
||||
|
||||
##### 기본 설정
|
||||
스프링 시큐리티는 `HttpSecurity`를 사용해 URL 기반 접근 제어를 설정합니다. 기본적으로 모든 요청을 인증된 사용자만 접근 가능하도록 설정할 수 있습니다.
|
||||
|
||||
```java
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService() {
|
||||
var user = User.withUsername("user")
|
||||
.password("{noop}password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
var admin = User.withUsername("admin")
|
||||
.password("{noop}adminpass")
|
||||
.roles("ADMIN")
|
||||
.build();
|
||||
return new InMemoryUserDetailsManager(user, admin);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/admin/**").hasRole("ADMIN") // /admin 하위는 ADMIN만 접근
|
||||
.requestMatchers("/user/**").hasRole("USER") // /user 하위는 USER만 접근
|
||||
.requestMatchers("/public/**").permitAll() // /public은 누구나 접근 가능
|
||||
.anyRequest().authenticated() // 나머지 요청은 인증 필요
|
||||
)
|
||||
.formLogin(); // 기본 로그인 폼 활성화
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### 주요 메서드 설명
|
||||
- **`requestMatchers()`**: 특정 URL 패턴을 지정합니다. 와일드카드(`**`, `*`)를 사용해 여러 경로를 매핑할 수 있습니다.
|
||||
- **`hasRole()`**: 주어진 역할이 있는 사용자만 접근을 허용합니다. `ROLE_` 접두사는 자동으로 추가됩니다(예: `hasRole("ADMIN")` → `ROLE_ADMIN`).
|
||||
- **`permitAll()`**: 인증 없이 누구나 접근 가능하도록 설정합니다.
|
||||
- **`anyRequest()`**: 위에서 정의되지 않은 모든 요청에 대해 적용할 규칙을 지정합니다.
|
||||
- **`authenticated()`**: 인증된 사용자만 접근 가능하도록 설정합니다.
|
||||
|
||||
##### 동작 원리
|
||||
1. 사용자가 `/admin/dashboard`에 접근하면, `FilterSecurityInterceptor`가 요청을 가로챕니다.
|
||||
2. `SecurityConfig`에 정의된 규칙을 확인해 사용자가 `ROLE_ADMIN` 역할을 가졌는지 검사합니다.
|
||||
3. 역할이 일치하면 요청이 통과되고, 그렇지 않으면 `403 Forbidden` 오류가 발생하거나 로그인 페이지로 리다이렉트됩니다.
|
||||
|
||||
##### 권한 기반 제어
|
||||
역할 대신 권한으로 제어하려면 `hasAuthority()`를 사용합니다:
|
||||
```java
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/posts/write").hasAuthority("WRITE_POST")
|
||||
.requestMatchers("/posts/delete").hasAuthority("DELETE_POST")
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
```
|
||||
이 경우, `UserDetailsService`에서 사용자에게 `WRITE_POST`나 `DELETE_POST` 같은 권한을 부여해야 합니다.
|
||||
|
||||
##### 추가 설정
|
||||
- **순서 중요성**: 규칙은 위에서 아래로 평가되므로, 더 구체적인 규칙을 먼저 작성해야 합니다. 예를 들어, `/public/**`를 `anyRequest()`보다 뒤에 두면 적용되지 않습니다.
|
||||
- **로그인 페이지 접근 허용**: 기본 로그인 페이지(`/login`)는 `permitAll()`로 열어둬야 인증되지 않은 사용자도 접근할 수 있습니다.
|
||||
- **커스터마이징**: `.access()` 메서드를 사용하면 더 복잡한 조건(예: IP 주소나 시간 기반)을 추가할 수 있습니다.
|
||||
|
||||
##### 실습 예제
|
||||
1. `/public/welcome`은 누구나, `/user/profile`은 `USER`, `/admin/manage`는 `ADMIN`만 접근 가능하도록 설정하세요.
|
||||
2. 브라우저에서 각 URL에 접근해 결과를 확인하세요.
|
||||
3. `hasAuthority()`로 `MANAGE_USERS` 권한을 추가하고 테스트해보세요.
|
||||
|
||||
---
|
||||
|
||||
위 내용은 역할과 권한의 차이를 명확히 하고, URL 기반 접근 제어의 설정과 동작을 실습 가능하도록 설명했습니다. 추가적인 코드 예제나 세부 사항이 필요하면 말씀해 주세요!
|
||||
Reference in New Issue
Block a user