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