1. 정의

    1. 이거는 Spring Security에서 메서드 단위 접근 제어를 활성화하는 설정 어노테이션, URL 단위 보안이 아니라 Service / Controller 메서드 단위로 권한을 검사하게 만드는 설정
    2. 즉, Spring Security는 기본적으로 URL 기반 보안을 하는데 이것만으로 부족한 경우가 있다. 같은 URL이라도 데이터 소유자가 다를 수 있음 Service 계층에서 추가 검증이 필요함
    3. Spring Boot 3 / Spring Security 6 부터 사용하는 표준 방식이다.
    4. 이 어노테이션이 있어야만이 @PreAuthorize, @PostAuthorize 같은 메서드 어노테이션이 실제로 작동한다.
  2. 장점

    1. URL보안의 한계를 보완한다
      1. 같은 url이라도 접근하는 데이터의 소유자가 다를 수 있음
      2. 그래서 Service 계층에서 추가 검증이 필요
      3. 이 문제를 해결하기 위해 메서드 보안 단위(ex) @PreAuthorize())를 사용하는데 그걸 활성화하는 어노테이션이 @EnableMethodSecurity이다.
  3. 예시

    1. 기본 설정 예시
    @Configuration
    @EnableMethodSecurity   //메서드 보안 단위 활성화
    public class SecurityConfig{
    
    	@Bean
    	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    		http.authorizeHttpRequests(auth -> auth.requestMatchers("/admin/**").hasRole("ADMIN")
    		.anyRequest().authenticated())
    		.formLogin();
    		return http.build();
    
    }
    
    
  4. Controller에서 사용하는 예시

    @RestController
    @RequestMapping("/users")
    public class UserController{
    	
    	@GetMapping("/{id}")
    	@PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')") 
    	//본인 정보는 접근 가능 and 관리자도 접근 가능
    	//그외는 403 Forbidden
    	public String getUser(@PathVariable Long id){
    		return "User Info";
    	}
    }
    
  5. Service 계층에서 사용하는 예시(실무에서 추천하는 것임)

    //실무에서는 Controller보다 Serviced에 권한을 거는 것이 더 안전하다.
    @Service
    public class UserService{
    
    	@PreAuthorize("hasRole('ADMIN')")
    	public void deleteUser(User id){
    		System.out.println("삭제완료");
    	}
    }
    
  6. Bean 호출 방식 예시 (실무 추천)

    @Service
    public class AuthService {
    	public boolean isOwner(Long userId){
    		Long loginUserId = getLoginUserId();
    		return userId.equals(loginUserId);
    	}
    	
    	private Long getLoginUserId(){
    		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    		CustomUser user = {CustomUser} aitj.getPrincipal();
    		return user.getId();
    	}
    }
    
    @PreAuthorize("@authService.isOwner(#userId) or hasRole('ADMIN')")
    public void updateUser(Long userId) {
    }
    
  7. 전체 실행 흐름

    클라이언트 요청
    			|
    FilterChain (URL 보안 검사)
    			|
    Controller 호출
    			| 
    Service 호출
    			|
    @PreAuthorize가 프록시에서 가로채서 권한 검사
    			|
    통과 -> 메서드 실행
    거부 -> 403발생