111 lines
7.4 KiB
Markdown
111 lines
7.4 KiB
Markdown
아래는 "스프링 시큐리티" 책의 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 기반 접근 제어의 설정과 동작을 실습 가능하도록 설명했습니다. 추가적인 코드 예제나 세부 사항이 필요하면 말씀해 주세요! |