2025-04-08T19:56:24
This commit is contained in:
136
docs/security/05_메서드 수준 보안.md
Normal file
136
docs/security/05_메서드 수준 보안.md
Normal file
@@ -0,0 +1,136 @@
|
||||
아래는 "스프링 시큐리티" 책의 5장에 포함될 "메서드 수준 보안 설정"과 "@PreAuthorize, @Secured 어노테이션 활용"에 대한 내용입니다. 이 설명은 개념을 명확히 하고 실무에서 사용할 수 있는 예제를 포함해 작성되었습니다.
|
||||
|
||||
---
|
||||
|
||||
### 5장. 인가와 권한 관리
|
||||
|
||||
#### 5.3 메서드 수준 보안 설정
|
||||
|
||||
URL 기반 접근 제어는 웹 요청 단위로 보안을 적용하는 데 유용하지만, 더 세밀한 제어가 필요한 경우가 있습니다. 예를 들어, 특정 비즈니스 로직이나 서비스 메서드에 접근을 제한하고 싶을 때 **메서드 수준 보안 설정**을 사용합니다. 스프링 시큐리티는 이를 위해 메서드 호출 시점에서 권한을 검사하는 기능을 제공하며, 주로 어노테이션 기반으로 구현됩니다.
|
||||
|
||||
##### 메서드 수준 보안의 필요성
|
||||
- **세밀한 제어**: URL 패턴만으로는 컨트롤러 내부 메서드의 개별 로직을 구분하기 어렵습니다.
|
||||
- **비즈니스 로직 보호**: 데이터베이스 작업이나 민감한 연산을 호출하는 메서드를 보호할 수 있습니다.
|
||||
- **재사용성**: 여러 엔드포인트에서 호출되는 서비스 메서드에 일관된 보안을 적용할 수 있습니다.
|
||||
|
||||
스프링 시큐리티는 메서드 수준 보안을 활성화하려면 `@EnableMethodSecurity`를 설정 클래스에 추가해야 합니다. 이후 `@PreAuthorize`, `@Secured` 같은 어노테이션을 사용해 권한을 검사합니다.
|
||||
|
||||
##### 기본 설정
|
||||
```java
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
|
||||
@Configuration
|
||||
@EnableMethodSecurity
|
||||
public class SecurityConfig {
|
||||
// 다른 설정 (UserDetailsService, SecurityFilterChain 등) 생략
|
||||
}
|
||||
```
|
||||
`@EnableMethodSecurity`는 메서드 보안을 활성화하며, 기본적으로 `@PreAuthorize`, `@PostAuthorize`, `@Secured` 어노테이션을 지원합니다.
|
||||
|
||||
#### 5.4 @PreAuthorize와 @Secured 활용
|
||||
|
||||
스프링 시큐리티는 메서드 수준 보안을 위해 두 가지 대표적인 어노테이션을 제공합니다: **`@Secured`**와 **`@PreAuthorize`**. 두 어노테이션은 비슷한 목적을 가지지만, 사용법과 유연성에서 차이가 있습니다.
|
||||
|
||||
##### @Secured 어노테이션
|
||||
`@Secured`는 메서드에 접근할 수 있는 역할(Role)을 지정하는 간단한 어노테이션입니다. 주로 역할 기반 접근 제어에 사용되며, 설정이 직관적입니다.
|
||||
|
||||
###### 사용 예제
|
||||
```java
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AdminService {
|
||||
|
||||
@Secured("ROLE_ADMIN")
|
||||
public String manageUsers() {
|
||||
return "User management page for admins only";
|
||||
}
|
||||
|
||||
@Secured({"ROLE_USER", "ROLE_ADMIN"})
|
||||
public String viewProfile() {
|
||||
return "Profile visible to users and admins";
|
||||
}
|
||||
}
|
||||
```
|
||||
- `@Secured("ROLE_ADMIN")`: `ROLE_ADMIN` 역할이 있는 사용자만 `manageUsers()` 메서드를 호출할 수 있습니다.
|
||||
- `@Secured({"ROLE_USER", "ROLE_ADMIN"})`: `ROLE_USER` 또는 `ROLE_ADMIN` 중 하나라도 가진 사용자가 `viewProfile()`를 호출할 수 있습니다.
|
||||
|
||||
###### 동작 원리
|
||||
1. 메서드 호출 시 스프링 시큐리티의 AOP(Aspect-Oriented Programming) 프록시가 개입합니다.
|
||||
2. 현재 인증된 사용자의 `GrantedAuthority` 목록을 확인해 지정된 역할이 있는지 검사합니다.
|
||||
3. 역할이 없으면 `AccessDeniedException`이 발생하고, 호출이 차단됩니다.
|
||||
|
||||
###### 한계
|
||||
- 역할만 지원하며, 권한(Authority)이나 복잡한 조건은 처리 불가.
|
||||
- SpEL(Spring Expression Language)을 지원하지 않아 유연성이 제한적.
|
||||
|
||||
##### @PreAuthorize 어노테이션
|
||||
`@PreAuthorize`는 `@Secured`보다 더 강력하고 유연한 어노테이션으로, SpEL을 사용해 복잡한 조건을 정의할 수 있습니다. 역할, 권한, 메서드 매개변수, 인증 객체 등을 기반으로 접근을 제어할 수 있습니다.
|
||||
|
||||
###### 사용 예제
|
||||
```java
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class PostService {
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public String deleteAllPosts() {
|
||||
return "All posts deleted by admin";
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('WRITE_POST')")
|
||||
public String createPost(String content) {
|
||||
return "Post created: " + content;
|
||||
}
|
||||
|
||||
@PreAuthorize("#username == authentication.name")
|
||||
public String viewOwnProfile(String username) {
|
||||
return "Profile for " + username;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('USER') and #postId > 0")
|
||||
public String editPost(int postId, String content) {
|
||||
return "Post " + postId + " updated: " + content;
|
||||
}
|
||||
}
|
||||
```
|
||||
- **`hasRole('ADMIN')`**: `ROLE_ADMIN` 역할 검사. (`@Secured`와 유사하지만 SpEL 사용 가능)
|
||||
- **`hasAuthority('WRITE_POST')`**: `WRITE_POST` 권한 검사.
|
||||
- **`#username == authentication.name`**: 메서드 매개변수(`username`)가 현재 인증된 사용자의 이름과 같은지 확인.
|
||||
- **`hasRole('USER') and #postId > 0`**: `ROLE_USER` 역할이 있고, `postId`가 양수일 때만 허용.
|
||||
|
||||
###### 동작 원리
|
||||
1. `@PreAuthorize`는 메서드 실행 전에 SpEL 표현식을 평가합니다.
|
||||
2. 표현식이 `true`면 메서드가 실행되고, `false`면 `AccessDeniedException`이 발생합니다.
|
||||
3. `authentication` 객체(현재 사용자 정보)와 메서드 매개변수를 활용해 동적 조건을 검사합니다.
|
||||
|
||||
###### 장점
|
||||
- **유연성**: 역할, 권한, 매개변수 기반의 복잡한 로직 가능.
|
||||
- **조건문**: `and`, `or`, `not` 같은 연산자 사용 가능.
|
||||
- **커스터마이징**: SpEL을 통해 비즈니스 로직에 맞춘 조건 설정 가능.
|
||||
|
||||
##### @Secured vs @PreAuthorize 비교
|
||||
| 특징 | `@Secured` | `@PreAuthorize` |
|
||||
|-------------------|---------------------------|---------------------------|
|
||||
| 지원 범위 | 역할(Role)만 | 역할, 권한, SpEL 조건 |
|
||||
| 유연성 | 낮음 | 높음 |
|
||||
| 사용 난이도 | 간단 | 약간 복잡 |
|
||||
| 예제 | `@Secured("ROLE_ADMIN")` | `@PreAuthorize("hasRole('ADMIN') and #id > 0")` |
|
||||
|
||||
##### 실습 예제
|
||||
1. 컨트롤러에 `@Secured("ROLE_USER")`를 추가해 특정 엔드포인트 보호.
|
||||
2. 서비스 메서드에 `@PreAuthorize("#id == authentication.principal.username")`를 적용해 사용자 본인의 데이터만 수정 가능하도록 설정.
|
||||
3. `ROLE_ADMIN`과 `MANAGE_USERS` 권한을 가진 사용자로 로그인해 두 어노테이션의 동작 확인.
|
||||
|
||||
##### 주의사항
|
||||
- **성능**: 메서드 수준 보안은 AOP 프록시를 통해 동작하므로, 과도한 사용은 성능에 영향을 줄 수 있습니다.
|
||||
- **예외 처리**: `AccessDeniedException`을 적절히 핸들링해 사용자 친화적인 오류 메시지를 제공하세요.
|
||||
- **설정 활성화**: `@EnableMethodSecurity`가 없으면 어노테이션이 동작하지 않으니 반드시 추가하세요.
|
||||
|
||||
---
|
||||
|
||||
위 내용은 메서드 수준 보안의 필요성과 `@Secured`, `@PreAuthorize` 어노테이션의 활용 방법을 체계적으로 설명했습니다. 추가 예제나 특정 상황에 대한 설명이 필요하면 말씀해 주세요!
|
||||
Reference in New Issue
Block a user