본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
4장 : 인증과 권한 부여

URL 규칙과 메서드 보안

URL 규칙만으로는 HTTP 밖에서 시작된 호출을 보호할 수 없습니다. scheduler, message consumer, 다른 bean은 filter chain을 지나지 않고 service를 호출합니다. HTTP method 규칙은 외부 경계에, @PreAuthorize는 유스케이스 경계에 두어 어느 진입점에서든 같은 study.read/study.write 언어를 쓰게 합니다.


인가 규칙은 두 경계에서 같은 언어를 쓴다

GET을 read로, 상태 변경을 write로 나눈 규칙은 URL과 service에서 동일해야 합니다. 두 층의 역할은 다릅니다. URL 인가는 비용이 큰 Controller 처리 전에 빠르게 거부하고, method 인가는 호출 경로가 늘어나도 업무 규칙을 보존합니다.


읽기와 쓰기 권한을 서비스까지 연결한다

@EnableMethodSecurity는 Spring AOP proxy가 @PreAuthorize를 읽도록 합니다. 따라서 같은 클래스 안의 self-invocation이나 new로 직접 만든 객체는 보안 proxy를 우회합니다. 예제 service를 Spring bean으로 두는 이유이며, 5장에서 실제 REST·GraphQL 공용 service를 만들 때 이 annotation을 그 interface로 옮깁니다.

src/main/java/com/andongmin/studylog/security/MethodSecurityConfiguration.java
package com.andongmin.studylog.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@Configuration(proxyBeanMethods = false)
@EnableMethodSecurity
public class MethodSecurityConfiguration {
}
src/main/java/com/andongmin/studylog/session/SecuredStudySessionService.java
package com.andongmin.studylog.session;

import java.util.List;
import java.util.UUID;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class SecuredStudySessionService {
    private final StudySessionJpaRepository repository;

    public SecuredStudySessionService(StudySessionJpaRepository repository) {
        this.repository = repository;
    }

    @PreAuthorize("hasAuthority('SCOPE_study.read')")
    public List<StudySessionEntity> findAll() {
        return repository.findAll();
    }

    @PreAuthorize("hasAuthority('SCOPE_study.write')")
    public void delete(UUID id) {
        repository.deleteById(id);
    }
}

우회 호출과 정상 호출을 비교한다

이 시점에서는 보안 annotation이 proxy 가능한 public method에 붙었는지를 컴파일로 확인합니다. 실제 URL 요청과 직접 service 호출의 차이는 나중의 MVC·GraphQL 검증에서 다룹니다. 중요한 것은 안전한 코드를 별도로 복제하는 것이 아니라, 실제 유스케이스 bean에 규칙을 두는 것입니다.

./mvnw -q -DskipTests compile
Scope 확인 결과
URL 바깥에서 서비스를 직접 호출해도 read 또는 write 권한 검사가 실행되어야 합니다.

서버 안쪽의 인가는 잡혔습니다. 이제 browser가 다른 origin에서 이 API를 호출할 때 어떤 요청을 읽게 할지, CSRF를 왜 끄는지를 위협 모델로 정합니다.