이 장에서는 요청에 대해 특정 권한 부여 제약 조건을 적용하는 방법에 대해 다룹니다. 프로덕션 환경에서는 모든 요청에 동일한 규칙을 적용하는 대신, 다양한 요청 그룹에 대해 맞춤형 접근 제어가 필요합니다. 예를 들어, 특정 사용자만 접근할 수 있는 엔드포인트가 있을 수 있고, 다른 엔드포인트는 모두 접근할 수 있도록 설정할 수 있습니다.
주요 내용:
- Matcher 메소드:
- 요청을 선택하여 권한을 부여하는 데 사용되는 matcher 메소드를 다룹니다.
- 예를 들어, anyRequest()는 모든 요청을 의미하며, 이는 이전 장에서 이미 사용된 메소드입니다. 모든 요청에 동일한 규칙을 적용할 때 사용됩니다.
- path 별로 요청 선택:
- 요청을 path에 따라 선택하는 방법을 설명합니다. 특정 경로에 대해 권한 부여를 설정할 수 있습니다.
- requestMatchers() 메소드를 사용하여 특정 path에 대한 요청을 선택하고 권한 부여 규칙을 적용합니다.
- HTTP 메소드 추가:
- 요청의 HTTP 메소드에 따라 권한을 다르게 설정할 수도 있습니다. 예를 들어, GET, POST, PUT, DELETE 등 각 메소드에 대해 다른 접근 제어를 적용할 수 있습니다.
- 비즈니스 요구 사항에 맞춘 커스텀 인증 구성:
- 각 애플리케이션은 비즈니스 요구 사항에 맞춰 접근 제어를 구성하므로, 모든 요청에 동일한 규칙을 적용하는 대신 세분화된 제어가 필요할 수 있습니다.
결론:
이 장에서는 요청을 선택하여 권한 부여 규칙을 적용하는 다양한 방법을 배우며, 이를 통해 애플리케이션에서 다양한 그룹의 요청에 맞춤형 제약 조건을 설정하는 방법을 익힙니다.
8.1 Using the requestMatchers() method to select endpoints
이 섹션에서는 requestMatchers() 메서드를 사용하여 특정 엔드포인트에 대한 권한 부여 규칙을 설정하는 방법을 배웁니다. 이를 통해 애플리케이션에서 요청에 대한 권한을 세밀하게 제어할 수 있습니다.
주요 내용:
- requestMatchers() 메서드 사용:
- requestMatchers() 메서드는 특정 경로(path)에 대한 요청을 선택하여 그에 대한 권한 부여 규칙을 설정하는 데 사용됩니다.
- 예를 들어, /hello 경로는 ADMIN 역할을 가진 사용자만 접근할 수 있도록 하고, /ciao 경로는 MANAGER 역할을 가진 사용자만 접근할 수 있도록 설정할 수 있습니다.
- 예제 코드 설명:
- 컨트롤러 클래스에서는 /hello와 /ciao 엔드포인트를 정의하고 있습니다.
- 구성 클래스에서 **InMemoryUserDetailsManager**를 사용하여 두 명의 사용자를 추가합니다. John은 ADMIN 역할을, Jane은 MANAGER 역할을 가집니다.
- securityFilterChain() 메서드에서는 requestMatchers()를 사용하여 /hello 경로는 ADMIN 역할을 가진 사용자만, /ciao 경로는 MANAGER 역할을 가진 사용자만 접근할 수 있도록 설정합니다. 그 외의 요청은 **permitAll()**을 사용하여 모두 허용합니다.
- 엔드포인트 테스트:
- John으로 /hello 엔드포인트에 접근하면 성공적인 응답을 받을 수 있지만, Jane은 HTTP 403 Forbidden 상태 코드를 받습니다.
- 반대로, Jane으로 /ciao 엔드포인트에 접근하면 성공적인 응답을 받고, John은 HTTP 403 Forbidden 상태를 받습니다.
- 새로운 엔드포인트 추가:
- /hola와 같은 새로운 엔드포인트를 추가하면, 기본적으로 누구나 접근할 수 있습니다. 이 엔드포인트는 **requestMatchers()**로 설정하지 않았기 때문에 인증 없이 접근할 수 있습니다.
- 만약 /hola 엔드포인트를 인증 없이 접근할 수 있도록 명확하게 설정하고 싶다면, permitAll() 메서드를 사용할 수 있습니다.
- 규칙 순서:
- 규칙을 적용할 때 anyRequest() 메서드는 반드시 더 일반적인 요청을 처리하는 마지막 규칙으로 작성해야 합니다. 즉, requestMatchers() 메서드를 통해 특정 경로를 먼저 정의하고, 그 이후에 anyRequest()를 사용하는 것이 중요합니다.
결론:
requestMatchers() 메서드를 사용하여 애플리케이션의 각 엔드포인트에 대해 세밀한 권한 부여 규칙을 적용할 수 있습니다. 이를 통해 애플리케이션에서 인증된 사용자와 역할에 따른 접근 제어를 정확하게 설정할 수 있습니다.
UNAUTHENTICATED VS. FAILED AUTHENTICATION
이 섹션에서는 **인증되지 않음(Unauthenticated)**과 인증 실패(Failed Authentication) 사이의 차이점을 설명합니다.
- 인증되지 않음 (Unauthenticated):
- 인증되지 않은 사용자가 접근할 수 있도록 설계된 엔드포인트는 사용자 이름과 비밀번호를 제공하지 않아도 호출할 수 있습니다. 이 경우, Spring Security는 인증을 수행하지 않습니다.
- 예를 들어, /hola 엔드포인트가 인증 없이 누구나 접근할 수 있도록 구성되었다면, 이 엔드포인트를 호출할 때는 인증이 필요 없으며, 요청은 정상적으로 처리됩니다. 응답 상태는 200 OK로 반환됩니다.
- 인증 실패 (Failed Authentication):
- 그러나 만약 인증되지 않은 요청에 사용자 이름과 비밀번호를 제공하면, Spring Security는 이를 평가하고 인증을 시도합니다. 이 경우 자격 증명이 잘못되었으면, 인증이 실패합니다.
- 예를 들어, /hola 엔드포인트에 잘못된 자격 증명을 제공하면, 인증 필터는 401 Unauthorized 상태로 응답합니다. 이는 인증이 실패한 경우입니다.
- 인증과 권한 부여의 순서:
- 인증은 항상 권한 부여 이전에 발생합니다. 즉, 요청이 먼저 인증 필터를 거쳐야 하며, 그 후에 권한 부여 필터가 처리됩니다. 인증이 실패하면, 요청은 권한 부여 필터로 전달되지 않으며, HTTP 401 Unauthorized 상태가 반환됩니다.
- permitAll() 메서드의 동작:
- permitAll() 메서드는 권한 부여 규칙에만 적용됩니다. 이 메서드는 인증과는 관련이 없습니다. 인증이 실패하면, 요청은 더 이상 허용되지 않으며 HTTP 401 상태 코드로 응답하게 됩니다. 인증이 실패하면 권한 부여 필터는 실행되지 않기 때문에, 인증이 실패한 요청은 애플리케이션에서 더 이상 처리되지 않습니다.
결론
- 인증되지 않음과 인증 실패는 다릅니다. 인증되지 않은 요청은 인증 과정 없이 처리될 수 있지만, 인증 실패는 자격 증명이 잘못된 경우 발생하며 401 Unauthorized 상태로 응답합니다.
- 인증이 실패하면 권한 부여는 진행되지 않으며, 애플리케이션은 요청을 처리하지 않습니다. permitAll() 메서드는 인증과는 관계없고, 인증이 실패하면 요청이 허용되지 않습니다.
8.5 Making Other Requests Accessible for All Authenticated Users
이 섹션에서는 인증된 사용자만 접근할 수 있도록 다른 엔드포인트에 대한 접근을 제한하는 방법을 다룹니다.
- authenticated() 메서드 사용:
- permitAll() 메서드는 인증 없이 접근 가능한 엔드포인트를 설정합니다. 하지만, authenticated() 메서드를 사용하면 모든 인증된 사용자만 접근할 수 있도록 설정할 수 있습니다. 이는 보안 구성에서 자주 사용되는 패턴으로, 인증된 사용자에게만 요청을 허용하는 방식입니다.
- 예시 코드에서는 /hello와 /ciao 엔드포인트는 각각 ADMIN과 MANAGER 역할을 가진 사용자만 접근할 수 있도록 설정하고, 그 외의 모든 요청은 인증된 사용자만 접근할 수 있도록 설정됩니다:
@Configuration public class ProjectConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.httpBasic(Customizer.withDefaults()); http.authorizeHttpRequests( c -> c.requestMatchers("/hello").hasRole("ADMIN") .requestMatchers("/ciao").hasRole("MANAGER") .anyRequest().authenticated() // A ); return http.build(); } }
- anyRequest().authenticated(): 이 설정은 /hello와 /ciao를 제외한 모든 경로는 인증된 사용자만 접근할 수 있도록 합니다.
- denyAll() 메서드 사용:
- 대신 denyAll() 메서드를 사용하면 다른 모든 요청을 거부할 수 있습니다. 이 방법은 특정 엔드포인트에 대한 접근을 명시적으로 차단하고, 나머지 요청을 모두 거부하는 방식입니다.
.anyRequest().denyAll() // 다른 모든 요청을 거부
- 경로와 HTTP 메서드 기반의 규칙 설정:
- 많은 실용적인 시나리오에서 여러 엔드포인트가 동일한 권한 부여 규칙을 공유할 수 있습니다. 하지만, 일부 엔드포인트에서는 경로뿐만 아니라 HTTP 메서드(GET, POST, DELETE 등)에 따라 다르게 규칙을 설정해야 할 수 있습니다. 예를 들어, 특정 경로에서 HTTP GET 요청은 허용하고, HTTP POST 요청은 거부하는 방식입니다.
결론
- authenticated() 메서드를 사용하면 인증된 사용자만 엔드포인트에 접근할 수 있도록 할 수 있습니다.
- denyAll() 메서드는 특정 요청을 모두 거부할 때 사용됩니다.
- 엔드포인트마다 경로와 HTTP 메서드에 따라 세밀한 권한 부여 규칙을 설정할 수 있습니다. 이처럼 requestMatchers()를 사용하여 다양한 조건에 맞춰 보안 설정을 할 수 있습니다.
물론, 다른 모든 엔드포인트를 인증된 사용자만 접근할 수 있도록 결정할 수 있습니다. 이를 위해, permitAll() 메소드를 authenticated()로 변경하면 됩니다. 이는 다음 목록에 제시되어 있습니다. 마찬가지로, denyAll() 메소드를 사용하여 다른 모든 요청을 거부할 수도 있습니다.
@Configuration
public class ProjectConfig {
// Omitted code
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.httpBasic(Customizer.withDefaults());
http.authorizeHttpRequests(
c -> c.requestMatchers("/hello").hasRole("ADMIN")
.requestMatchers("/ciao").hasRole("MANAGER")
//.anyRequest().denyAll()
.anyRequest().authenticated() # A
);
return http.build();
}
}
#A 다른 모든 요청은 인증된 사용자만 접근할 수 있습니다.
이 섹션의 마지막에서, 권한 부여 제한을 구성하고자 하는 요청을 참조하기 위해 matchers 메소드를 어떻게 사용해야 하는지에 대해 익숙해졌습니다. 이제 사용할 수 있는 문법에 대해 더 심도 있게 다뤄야 합니다. 대부분의 실용적인 시나리오에서, 여러 엔드포인트가 동일한 권한 부여 규칙을 가질 수 있으므로, 엔드포인트별로 설정할 필요가 없습니다. 또한, 지금까지 해왔던 것처럼 경로뿐만 아니라 HTTP method를 명시할 필요가 있을 때도 있습니다. 때때로, 엔드포인트의 경로가 HTTP GET으로 호출될 때만 규칙을 구성해야 할 필요가 있습니다. 이 경우, HTTP POST와 HTTP DELETE에 대해 다른 규칙을 정의해야 합니다. 다음 섹션에서, 각 유형의 매처 메소드를 살펴보고 이러한 측면에 대해 자세히 논의합니다.
8.2 권한 부여 제한을 적용할 요청 선택하기
이 섹션에서는 request matchers를 구성하는 방법에 대해 깊이 다룹니다. requestMatchers() 메소드를 사용하는 것은 권한 부여 구성을 적용하기 위해 요청을 참조하는 일반적인 접근 방식입니다. 따라서 개발하는 애플리케이션에서 이 메소드를 요청을 참조하기 위해 많은 기회를 가질 것으로 기대됩니다.
이 matchers는 경로를 참조하기 위한 표준 ANT 문법을 사용합니다. 이 문법은 @RequestMapping, @GetMapping, @PostMapping 등과 같은 어노테이션으로 엔드포인트 매핑을 작성할 때 사용하는 것과 동일합니다. MVC matchers를 선언하기 위해 사용할 수 있는 두 가지 방법은 다음과 같습니다:
- requestMatchers(HttpMethod method, String... patterns) - 제한을 적용할 HTTP method와 경로를 모두 지정할 수 있게 해줍니다. 이 메소드는 같은 경로에 대해 다른 HTTP method별로 다른 제한을 적용하고 싶을 때 유용합니다.
- requestMatchers(String... patterns) - 경로 기반으로 권한 부여 제한을 적용할 필요가 있을 때 사용하기 더 간단하고 쉽습니다. 이 제한은 경로와 함께 사용되는 모든 HTTP method에 자동으로 적용될 수 있습니다.
이 섹션에서는 requestMatchers() 메소드를 사용하는 여러 방법을 다룹니다. 이를 보여주기 위해, 여러 엔드포인트를 노출하는 애플리케이션을 작성하기로 시작합니다. 처음으로, GET 이외의 다른 HTTP method로 호출할 수 있는 엔드포인트를 작성합니다. 지금까지 다른 HTTP method를 사용하는 것을 피한 것을 관찰했을 수 있습니다. 이유는 Spring Security가 기본적으로 크로스 사이트 요청 위조(CSRF)에 대한 보호를 적용하기 때문입니다. 9장에서는 Spring Security가 CSRF 토큰을 사용하여 이 취약점을 어떻게 완화하는지 논의할 것입니다. 하지만 현재 예제를 간단하게 하고 POST, PUT, 또는 DELETE로 노출된 모든 엔드포인트를 호출할 수 있도록 하기 위해, 우리는 configure() 메소드에서 CSRF 보호를 비활성화할 필요가 있습니다:
http.csrf(c -> c.disable());
엔드포인트 정의
우리의 테스트에 사용할 네 개의 엔드포인트를 정의하기로 시작합니다:
- HTTP 메소드 GET을 사용하는 /a
- HTTP 메소드 POST를 사용하는 /a
- HTTP 메소드 GET을 사용하는 /a/b
- HTTP 메소드 GET을 사용하는 /a/b/c
@RestController
public class TestController {
@PostMapping("/a")
public String postEndpointA() {
return "Works!";
}
@GetMapping("/a")
public String getEndpointA() {
return "Works!";
}
@GetMapping("/a/b")
public String getEnpointB() {
return "Works!";
}
@GetMapping("/a/b/c")
public String getEnpointC() {
return "Works!";
}
}
UserDetailsService 정의
다른 Role을 가진 몇 명의 사용자도 필요합니다. 일을 간단하게 유지하기 위해, 우리는 InMemoryUserDetailsManager를 사용합니다.
@Configuration
public class ProjectConfig {
@Bean
public UserDetailsService userDetailsService() {
var manager = new InMemoryUserDetailsManager();
var user1 = User.withUsername("john")
.password("12345")
.roles("ADMIN")
.build();
var user2 = User.withUsername("bill")
.password("12345")
.roles("MANAGER")
.build();
manager.createUser(user1);
manager.createUser(user2);
return manager;
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
첫 번째 시나리오 구성
첫 번째 시나리오부터 시작해봅시다. /a 경로에 대해 HTTP GET 메소드로 수행된 요청의 경우, 애플리케이션은 사용자를 인증해야 합니다. 같은 경로에 대해 HTTP POST 메소드를 사용하는 요청은 인증을 요구하지 않습니다. 애플리케이션은 다른 모든 요청을 거부합니다. 다음 리스팅은 이 설정을 달성하기 위해 작성해야 할 구성을 보여줍니다.
@Configuration
public class ProjectConfig {
// Omitted code
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.httpBasic(Customizer.withDefaults());
http.authorizeHttpRequests(
c -> c.requestMatchers(HttpMethod.GET, "/a")
.authenticated() # A
.requestMatchers(HttpMethod.POST, "/a")
.permitAll() # B
.anyRequest()
.denyAll() # C
);
http.csrf(
c -> c.disable() # D
);
return http.build();
}
}
- #A /a 경로에 대한 HTTP GET 메소드로 호출된 요청의 경우, 앱은 사용자를 인증해야 합니다.
- #B HTTP POST 메소드로 호출된 /a 경로의 요청을 누구에게나 허용합니다.
- #C 다른 모든 경로로의 다른 모든 요청을 거부합니다.
- #D HTTP POST 메소드를 사용하여 /a 경로로의 호출을 가능하게 하기 위해 CSRF를 비활성화합니다.
결과 분석
다음 코드 스니펫에서, 우리는 리스팅 8.8에 제시된 구성에 대한 엔드포인트로의 호출 결과를 분석합니다. 인증 없이 HTTP 메소드 POST를 사용하여 경로 /a로의 호출을 위해 다음 cURL 명령어를 사용하세요:
curl -XPOST http://localhost:8080/a
응답 본문:
Works!
HTTP GET을 사용하여 인증 없이 /a 경로를 호출하려면 다음 명령어를 사용하세요:
curl -XGET http://localhost:8080/a
응답:
{
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/a"
}
인증된 사용자로 요청하려면:
curl -u john:12345 -XGET http://localhost:8080/a
응답 본문:
Works!
하지만 사용자 John은 /a/b 경로를 호출할 수 없으므로, 이 호출을 위해 그의 자격 증명으로 인증하는 것은 403 Forbidden을 생성합니다:
curl -u john:12345 -XGET http://localhost:8080/a/b
응답:
{
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/a/b"
}
경로 표현식 사용
앞서 설명한 것처럼, 경로가 /a/b로 시작하는 모든 요청에 대해 동일한 규칙을 적용하고 싶다면, ** 연산자를 사용하여 경로 표현식을 정의할 수 있습니다.
@Configuration
public class ProjectConfig {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.httpBasic(Customizer.withDefaults());
http.authorizeHttpRequests(
c -> c.requestMatchers( "/a/b/**").authenticated() # A
.anyRequest().permitAll()
);
http.csrf(
c -> c.disable()
);
return http.build();
}
}
이 예제에서 /a/b/** 표현식은 /a/b로 시작하는 모든 경로를 참조합니다. 인증 없이 /a 경로를 호출할 수 있지만, /a/b로 시작하는 모든 경로에 대해서는 애플리케이션이 사용자를 인증해야 합니다.
이 예제는 requestMatchers() 메소드 사용 방법과 경로 표현식을 활용하여 권한 부여 규칙을 설정하는 데 필요한 기초적인 구성을 제공합니다.
8.2.1 정규 표현식과 요청 매처 사용
이 섹션에서는 정규 표현식(regex)에 대해 논의합니다. 정규 표현식이 무엇인지 이미 알고 있어야 하지만, 그 주제에 대한 전문가일 필요는 없습니다. https://www.regular-expressions.info/books.html에서 추천하는 책들 중 어느 것이든 주제를 더 깊이 배울 수 있는 훌륭한 자료입니다. 정규 표현식을 작성할 때, 저도 종종 온라인 생성기와 같은 도구를 자주 사용합니다
정규 표현식을 생성하는 방법을 배우려면 https://regexr.com/과 같은 온라인 생성기를 사용할 수 있습니다.
8.2 및 8.3 섹션에서, 대부분의 경우 경로 표현식 문법[/a, /a/*, /a/**]을 사용하여 권한 부여 구성을 적용할 요청을 참조할 수 있다는 것을 배웠습니다. 그러나 일부 경우에는 더 특별한 요구 사항이 있을 수 있으며, 이러한 요구 사항은 경로 표현식으로 해결할 수 없습니다. 이러한 요구 사항의 예는 다음과 같습니다: "경로에 특정 심볼이나 문자가 포함된 모든 요청을 거부한다." 이러한 시나리오에서는 정규 표현식과 같은 더 강력한 표현을 사용해야 합니다.
정규 표현식을 사용하여 문자열의 어떤 형식이든 표현할 수 있으므로, 이 문제에 대해 무한한 가능성을 제공합니다. 하지만 간단한 시나리오에 적용되었을 때조차 읽기 어렵다는 단점이 있습니다. 이러한 이유로, 경로 표현식을 사용하고 다른 선택지가 없을 때만 정규 표현식으로 돌아가는 것을 선호할 수 있습니다. 정규 표현식 요청 매처를 구현하기 위해, 파라미터로 RegexRequestMatcher 구현체와 함께 requestMatchers() 메소드를 사용할 수 있습니다.
정규 표현식[regex matcher]가 어떻게 작동하는지 증명하기 위해, 예제를 통해 실제로 적용해 보겠습니다: 사용자에게 비디오 콘텐츠를 제공하는 애플리케이션을 구축합니다. 비디오 콘텐츠를 제시하는 애플리케이션은, 사용자가 /video/{country}/{language} 엔드포인트를 호출하여 콘텐츠를 제공받을 수 있도록 합니다. 예를 들어, 애플리케이션은 사용자가 요청을 하는 곳에서 두 개의 경로 변수로 국가와 언어를 받습니다. 미국, 캐나다, 또는 영국에서 요청이 오거나 영어를 사용하는 경우, 어떤 인증된 사용자든 비디오 콘텐츠를 볼 수 있다고 간주합니다.
이 예제는 프로젝트 ssia-ch8-ex5에서 구현된 것을 찾을 수 있습니다. 보안이 필요한 엔드포인트에는 다음 리스팅에서 보여주는 것처럼 두 개의 경로 변수가 있습니다. 이것은 request matcher를 사용하여 구현하기 복잡한 요구 사항을 만듭니다.
Listing 8.12 The definition of the endpoint for the controller class
@RestController
public class VideoController {
@GetMapping("/video/{country}/{language}")
public String video(@PathVariable String country,
@PathVariable String language) {
return "Video allowed for " + country + " " + language;
}
}
단일 경로 변수에 대한 조건의 경우, 경로 표현식에서 직접 정규 표현식을 작성할 수 있습니다. 우리는 섹션 8.2에서 이러한 예를 언급했지만, 그 당시에는 정규 표현식에 대해 깊이 논의하지 않았기 때문에 자세히 다루지 않았습니다.
엔드포인트 /email/{email}이 있고, 이메일 파라미터의 값으로 .com으로 끝나는 주소를 보내는 요청에만 matcher를 사용하여 규칙을 적용하고 싶다고 가정해 봅시다. 그 경우, 다음 코드 스니펫에 제시된 대로 요청 매처를 작성합니다. 이에 대한 전체 예제는 프로젝트 ssia-ch8-ex6에서 찾을 수 있습니다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.httpBasic(Customizer.withDefaults());
http.authorizeHttpRequests(
c -> c.requestMatchers("/email/{email:.*(?:.+@.+\\.com)}")
.permitAll()
.anyRequest().denyAll()
);
return http.build();
}
이러한 제한을 테스트하면, 애플리케이션이 .com으로 끝나는 이메일만을 허용하는 것을 확인할 수 있습니다. 예를 들어, jane@example.com으로 엔드포인트를 호출하려면 다음 명령을 사용할 수 있습니다:
curl http://localhost:8080/email/jane@example.com
그리고 jane@example.net으로 호출할 경우 응답 본문은 다음과 같습니다:
{
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/email/jane@example.net"
}
이는 상당히 쉽고, 왜 우리가 정규 표현식 매처를 덜 자주 접하는지 더 명확하게 이해할 수 있게 해줍니다. 하지만, 앞서 말했듯이, 요구 사항이 복잡한 경우도 있습니다. 다음과 같은 상황을 발견했을 때 정규 표현식 매처를 사용하는 것이 더 편리하다는 것을 알게 될 것입니다:
- 전화번호나 이메일 주소를 포함하는 모든 경로에 대한 특정 구성
- 모든 경로 변수를 통해 전송되는 것을 포함하여 특정 형식을 가진 모든 경로에 대한 특정 구성
우리의 정규 표현식 matcher 예제(ssia-ch8-ex6)로 돌아가보겠습니다. 더 복잡한 규칙을 작성해야 하고, 결국에는 더 많은 경로 패턴과 여러 경로 변수 값에 대한 참조가 필요할 때는, 정규 표현식 매처를 작성하는 것이 더 쉽습니다. 목록 8.13에서는 /video/{country}/{language} 경로에 대한 요구 사항을 해결하기 위해 정규 표현식 matcher를 사용하는 설정 클래스의 정의를 찾을 수 있습니다. 또한, 구현을 테스트하기 위해 서로 다른 권한을 가진 두 개의 사용자를 추가합니다.
Listing 8.13 The configuration class using a regex matcher
@Configuration
public class ProjectConfig {
@Bean
public UserDetailsService userDetailsService() {
var uds = new InMemoryUserDetailsManager();
var u1 = User.withUsername("john")
.password("12345")
.authorities("read")
.build();
var u2 = User.withUsername("jane")
.password("12345")
.authorities("read", "premium")
.build();
uds.createUser(u1);
uds.createUser(u2);
return uds;
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.httpBasic(Customizer.withDefaults());
http.authorizeHttpRequests(
c -> c.requestMatchers(
new RegexRequestMatcher(".*/[us|uk|ca]+/[en|fr].*",
HttpMethod.GET.name()))
.authenticated()
.anyRequest()
.hasAuthority("premium")
);
return http.build();
}
}
정규 표현식은 강력한 도구입니다. 정규 표현식을 사용하여 어떠한 요구 사항에 대한 경로를 참조할 수 있습니다. 그러나 정규 표현식은 읽기 어렵고 매우 길어질 수 있기 때문에, 마지막 선택으로 남겨두어야 합니다. 경로 표현식이 문제에 대한 해결책을 제공하지 않는 경우에만 사용하세요.
8.3 요약
실제 시나리오에서는 다양한 요청에 대해 서로 다른 권한 부여 규칙을 적용하는 경우가 많습니다. 권한 부여 규칙은 주로 요청의 **경로(Path)**와 HTTP 메소드를 기반으로 구성됩니다. 이를 위해 requestMatchers() 메서드를 사용하여 특정 요청에 대한 권한을 설정할 수 있습니다.
하지만, 요구 사항이 너무 복잡하여 경로 표현식만으로는 해결할 수 없는 경우, **정규 표현식(Regex)**을 사용하여 더 강력하게 구현할 수 있습니다. 정규 표현식을 활용하면 경로와 파라미터에 대한 더욱 정밀한 조건을 설정할 수 있어, 복잡한 권한 부여 규칙을 효과적으로 처리할 수 있습니다.
'Spring Security in Action' 카테고리의 다른 글
10장 Configuring Cross-Origin Resource Sharing(CORS) (0) | 2024.12.01 |
---|---|
9장 Configuring Cross-Site Request Forgery(CSRF) protection (0) | 2024.12.01 |
7장 Configuring endpoint-level authorization: Restricting access (0) | 2024.12.01 |
5장 A web app's security begins with filters (0) | 2024.12.01 |
4장 비밀번호 인코더 구현 및 작업 (0) | 2024.12.01 |