ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,20 @@
+package nextstep.security.authorization;
+
+import jakarta.servlet.http.HttpServletRequest;
+import nextstep.security.authentication.Authentication;
+
+public class AuthenticatedAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
+
+    @Override
+    public AuthorizationDecision check(Authentication authentication, HttpServletRequest object) {
+        if (authentication == null) {
+            return AuthorizationDecision.unAuthorizationDecision();
+        }
+
+        if (authentication.isAuthenticated()) {
+            return AuthorizationDecision.authorizationDecision();
+        }
+
+        return AuthorizationDecision.unAuthorizationDecision();
+    }
+}

리뷰: 혹시나 Authentication가 없는 요청이 들어올 것을 생각해서 해당 로직을 작성했었는데..
사실 인가 확인하는 부분에는 모두 인증이 되었다고 판단하는게 맞겠죠...? 😅
@@ -9,30 +9,25 @@
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.Set;
 
 public class CheckAuthenticationFilter extends OncePerRequestFilter {
-    private static final String DEFAULT_REQUEST_URI = "/members";
+    private final AuthorizationManager<HttpServletRequest> authorizationManager;
+
+    public CheckAuthenticationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
+        this.authorizationManager = authorizationManager;
+    }
 
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
-        if (!DEFAULT_REQUEST_URI.equals(request.getRequestURI())) {
-            filterChain.doFilter(request, response);
-            return;
-        }
-
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (authentication == null || !authentication.isAuthenticated()) {
-            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-            return;
-        }
 
-        Set<String> authorities = authentication.getAuthorities();
-        if (!authorities.contains("ADMIN")) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        AuthorizationDecision decision = authorizationManager.check(authentication, request);
+
+        if (decision.isAuthorization()) {
+            filterChain.doFilter(request, response);
             return;
         }
 
-        filterChain.doFilter(request, response);
+        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
     }
 }

리뷰: 앗~ 전혀 몰랐네요;;;! 😅
@@ -1,7 +1,6 @@
 package nextstep.security.authorization;
 
 import nextstep.security.authentication.Authentication;
-import nextstep.security.authentication.AuthenticationException;
 import nextstep.security.context.SecurityContextHolder;
 import org.aopalliance.aop.Advice;
 import org.aopalliance.intercept.MethodInterceptor;
@@ -16,21 +15,21 @@
 public class SecuredMethodInterceptor implements MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
 
     private final Pointcut pointcut;
+    private final AuthorizationManager<MethodInvocation> authorizationManager;
 
-    public SecuredMethodInterceptor() {
+    public SecuredMethodInterceptor(AuthorizationManager<MethodInvocation> authorizationManager) {
         this.pointcut = new AnnotationMatchingPointcut(null, Secured.class);
+        this.authorizationManager = authorizationManager;
     }
 
     @Override
     public Object invoke(MethodInvocation invocation) throws Throwable {
         Method method = invocation.getMethod();
         if (method.isAnnotationPresent(Secured.class)) {
-            Secured secured = method.getAnnotation(Secured.class);
             Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-            if (authentication == null) {
-                throw new AuthenticationException();
-            }
-            if (!authentication.getAuthorities().contains(secured.value())) {
+            AuthorizationDecision authorizationDecision = authorizationManager.check(authentication,invocation);
+
+            if (!authorizationDecision.isAuthorization()) {
                 throw new ForbiddenException();
             }
         }

리뷰: AuthorizationManager 내부에서 Authentication를 null 체크 한다고 해당 로직을 제거 했었는데,
SecuredAuthorizationManager가 인가만을 체크하는 것이기 때문에.. 해당 로직이 남아 있는게 맞아보이네욥 ㅠ0ㅠ
@@ -0,0 +1,20 @@
+package nextstep.security.authorization;
+
+import jakarta.servlet.http.HttpServletRequest;
+import nextstep.security.authentication.Authentication;
+
+public class AuthenticatedAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
+
+    @Override
+    public AuthorizationDecision check(Authentication authentication, HttpServletRequest object) {
+        if (authentication == null) {
+            return AuthorizationDecision.unAuthorizationDecision();
+        }
+
+        if (authentication.isAuthenticated()) {
+            return AuthorizationDecision.authorizationDecision();
+        }
+
+        return AuthorizationDecision.unAuthorizationDecision();
+    }
+}

리뷰: 605be945e21c24abe9040c8cbdad211f05132edc 해당 부분을 통해 반영하였습니다!
@@ -9,30 +9,25 @@
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.Set;
 
 public class CheckAuthenticationFilter extends OncePerRequestFilter {
-    private static final String DEFAULT_REQUEST_URI = "/members";
+    private final AuthorizationManager<HttpServletRequest> authorizationManager;
+
+    public CheckAuthenticationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
+        this.authorizationManager = authorizationManager;
+    }
 
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
-        if (!DEFAULT_REQUEST_URI.equals(request.getRequestURI())) {
-            filterChain.doFilter(request, response);
-            return;
-        }
-
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (authentication == null || !authentication.isAuthenticated()) {
-            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-            return;
-        }
 
-        Set<String> authorities = authentication.getAuthorities();
-        if (!authorities.contains("ADMIN")) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        AuthorizationDecision decision = authorizationManager.check(authentication, request);
+
+        if (decision.isAuthorization()) {
+            filterChain.doFilter(request, response);
             return;
         }
 
-        filterChain.doFilter(request, response);
+        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
     }
 }

리뷰: bd27412e9a027651567f25f0e51b725454af6e8c 를 통해 호다닥 수정 하였습니다!
@@ -0,0 +1,23 @@
+package nextstep.security.authorization;
+
+import jakarta.servlet.http.HttpServletRequest;
+import nextstep.security.authentication.Authentication;
+
+public class RequestMatcherEntry<T extends AuthorizationManager> {
+
+    private final RequestMatcher requestMatcher;
+    private final T entry;
+
+    public RequestMatcherEntry(RequestMatcher requestMatcher, T entry) {
+        this.requestMatcher = requestMatcher;
+        this.entry = entry;
+    }
+
+    public boolean isMatches(HttpServletRequest request) {
+        return requestMatcher.matches(request);
+    }
+
+    public AuthorizationDecision check(Authentication authentication, HttpServletRequest request) {
+        return entry.check(authentication, request);
+    }
+}

리뷰: 682a3ed6d8e7087121230ad169934e5193a5c73b 를 통해 처리를 해 보았습니다.
@@ -0,0 +1,23 @@
+package nextstep.security.authorization;
+
+import nextstep.security.authentication.Authentication;
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.lang.reflect.Method;
+
+public class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
+
+    @Override
+    public AuthorizationDecision check(Authentication authentication, MethodInvocation invocation) {
+        Method method = invocation.getMethod();
+        Secured secured = method.getAnnotation(Secured.class);
+        if (authentication == null) {
+            return AuthorizationDecision.unAuthorizationDecision();
+        }
+        if (!authentication.getAuthorities().contains(secured.value())) {
+            return AuthorizationDecision.unAuthorizationDecision();
+        }
+
+        return AuthorizationDecision.authorizationDecision();
+    }
+}

리뷰: 589bd7f23cba24fe7845d0c83f8227d0d3c10b83 에서 처리하였습니다.
페어로 개발 할 때, 페어짝이 짜주신거였는데 😅 
성공의 경우에도, 팩토리 메서드를 통해 생성될 수 있도록 수정하였습니다
@@ -0,0 +1,29 @@
+package nextstep.security.authorization;
+
+import jakarta.servlet.http.HttpServletRequest;
+import nextstep.security.authentication.Authentication;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DenyAllAuthorizationManagerTest {
+
+
+    private AuthorizationManager<HttpServletRequest> manager = new DenyAllAuthorizationManager<>();
+
+    @Test
+    @DisplayName("어떠한 요청이든, 거부합니다.")
+    void deny() {
+        //given
+        final Authentication authentication = null;
+        final MockHttpServletRequest request = new MockHttpServletRequest();
+
+        //when
+        final boolean result = manager.check(authentication, request).isAuthorization();
+
+        //then
+        assertThat(result).isFalse();
+    }
+}

리뷰: 테스트 👍👍👍
@@ -0,0 +1,20 @@
+package nextstep.security.authorization;
+
+import jakarta.servlet.http.HttpServletRequest;
+import nextstep.security.authentication.Authentication;
+
+public class AuthenticatedAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
+
+    @Override
+    public AuthorizationDecision check(Authentication authentication, HttpServletRequest object) {
+        if (authentication == null) {
+            return AuthorizationDecision.unAuthorizationDecision();
+        }
+
+        if (authentication.isAuthenticated()) {
+            return AuthorizationDecision.authorizationDecision();
+        }
+
+        return AuthorizationDecision.unAuthorizationDecision();
+    }
+}

리뷰: > 혹시나 Authentication가 없는 요청이 들어올 것을 생각해서 해당 로직을 작성했었는데..
사실 인가 확인하는 부분에는 모두 인증이 되었다고 판단하는게 맞겠죠...? 😅

넵!! 보통 인가 로직에서는 인증이 완료료된 상태라고 가정하는게 좋고, 만약 null이 올 수 있는면 추상화 레벨을 생각해서 다른 예외가 발생하는 것이 더 자연스러울 것 같습니다 ㅎㅎ
@@ -24,7 +24,10 @@ public enum ErrorCode {
     REVIEW_NOT_FOUND(HttpStatus.NOT_FOUND, "리뷰가 존재하지 않습니다."),
 
     // 팬트리 관련 익셉션
-    PANTRY_NOT_FOUND(HttpStatus.NOT_FOUND, "팬트리가 존재하지 않습니다.");
+    PANTRY_NOT_FOUND(HttpStatus.NOT_FOUND, "팬트리가 존재하지 않습니다."),
+
+    // 레시피 관련 익셉션
+    RECIPE_NOT_FOUND(HttpStatus.NOT_FOUND, "레시피가 존재하지 않습니다.");
 
     private final HttpStatus httpStatus;
     private final String message;

리뷰: 요거 빼주세요
@@ -1,5 +1,18 @@
 package salute.oneshot.domain.recipeReview.dto.request;
 
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import org.hibernate.validator.constraints.Range;
+
+@Getter
 public class CreateRecipeReviewRequestDto {
 
+    @NotNull
+    @Range(min = 0, max = 5)
+    private Byte star;
+
+    @NotBlank(message = "설명은 필수입니다.")
+    private String content;
+
 }

리뷰: 리뷰에서 name은 다 빼주시면 될 것 같아요
@@ -1,13 +1,19 @@
 package nextstep.app;
 
+import jakarta.servlet.http.HttpServletRequest;
 import nextstep.app.domain.Member;
 import nextstep.app.domain.MemberRepository;
 import nextstep.security.authentication.AuthenticationException;
 import nextstep.security.authentication.BasicAuthenticationFilter;
 import nextstep.security.authentication.UsernamePasswordAuthenticationFilter;
-import nextstep.security.authorization.CheckAuthenticationFilter;
-import nextstep.security.authorization.SecuredAspect;
+import nextstep.security.authorization.AuthorizationFilter;
 import nextstep.security.authorization.SecuredMethodInterceptor;
+import nextstep.security.authorization.manager.AuthenticatedAuthorizationManager;
+import nextstep.security.authorization.manager.AuthorityAuthorizationManager;
+import nextstep.security.authorization.manager.AuthorizationManager;
+import nextstep.security.authorization.manager.DenyAllAuthorizationManager;
+import nextstep.security.authorization.manager.PermitAllAuthorizationManager;
+import nextstep.security.authorization.manager.RequestAuthorizationManager;
 import nextstep.security.config.DefaultSecurityFilterChain;
 import nextstep.security.config.DelegatingFilterProxy;
 import nextstep.security.config.FilterChainProxy;
@@ -22,6 +28,10 @@
 import java.util.List;
 import java.util.Set;
 
+import static nextstep.security.authorization.matcher.RequestMatcherEntry.createDefaultMatcher;
+import static nextstep.security.authorization.matcher.RequestMatcherEntry.createMvcMatcher;
+import static org.springframework.http.HttpMethod.GET;
+
 @EnableAspectJAutoProxy
 @Configuration
 public class SecurityConfig {
@@ -46,19 +56,20 @@ public FilterChainProxy filterChainProxy(List<SecurityFilterChain> securityFilte
     public SecuredMethodInterceptor securedMethodInterceptor() {
         return new SecuredMethodInterceptor();
     }
-//    @Bean
-//    public SecuredAspect securedAspect() {
-//        return new SecuredAspect();
-//    }
 
     @Bean
     public SecurityFilterChain securityFilterChain() {
+        final AuthorizationManager<HttpServletRequest> authorizationManager = new RequestAuthorizationManager(List.of(
+                createMvcMatcher(GET, "/members", new AuthorityAuthorizationManager<>("ADMIN")),
+                createMvcMatcher(GET, "/members/me", new AuthenticatedAuthorizationManager<>()),
+                createMvcMatcher(GET, "/search", new PermitAllAuthorizationManager<>())
+        ), createDefaultMatcher(new DenyAllAuthorizationManager<>()));
         return new DefaultSecurityFilterChain(
                 List.of(
                         new SecurityContextHolderFilter(),
                         new UsernamePasswordAuthenticationFilter(userDetailsService()),
                         new BasicAuthenticationFilter(userDetailsService()),
-                        new CheckAuthenticationFilter()
+                        new AuthorizationFilter(authorizationManager)
                 )
         );
     }

리뷰: config에 잘 추가해주셨네요 👍 

자바 컨벤션 상 클래스 네이밍은 명사를 활용하기 때문에 `Has`보다는 조금 더 적절한 네이밍이 들어가면 좋을 것 같아요.
https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html
@@ -0,0 +1,8 @@
+package nextstep.security.authorization.manager;
+
+import nextstep.security.authentication.Authentication;
+
+@FunctionalInterface
+public interface AuthorizationManager<T> {
+    AuthorizationResult authorize(Authentication authentication, T target);
+}

리뷰: 최근 spring security 버전에서는 `check`는 사실 deprecated되었는데요. 일반적으로는 잘 아시다시피 대부분의 오픈소스는 결국 추상화를 바라보고 진행하려하며 `AuthorizationDecision`이 spring security 내에서는 클래스 구현체로 되어있고, 버전이 올라가는 과정에서 이부분 또한 추상화되었어요.

최근 버전에서는 그래서 구현체를 반환하는 `AuthorizationDecision`을 사용하기보다는 이를 사용하는 default 메소드인 `authorize` 사용을 권장하고 있습니다.

https://github.com/franticticktick/spring-security/blob/main/core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java

https://github.com/spring-projects/spring-security/pull/14712
https://github.com/spring-projects/spring-security/pull/14846
@@ -0,0 +1,8 @@
+package nextstep.security.authorization.manager;
+
+import nextstep.security.authentication.Authentication;
+
+@FunctionalInterface
+public interface AuthorizationManager<T> {
+    AuthorizationResult authorize(Authentication authentication, T target);
+}

리뷰: 선택 요구사항에는 `verify`도 있는데요. 정완님은 `verfiy`는 어떤 상황에서 사용하면 좋을만하다고 생각하시나요?
@@ -0,0 +1,22 @@
+package nextstep.security.authorization.manager;
+
+public enum AuthorizationDecision implements AuthorizationResult {
+    GRANTED(true), NOT_GRANTED(false);
+
+    private final boolean granted;
+
+    AuthorizationDecision(boolean granted) {
+        this.granted = granted;
+    }
+
+    public static AuthorizationDecision of(boolean granted) {
+        if (granted) {
+            return GRANTED;
+        }
+        return NOT_GRANTED;
+    }
+
+    public boolean isGranted() {
+        return this.granted;
+    }
+}

리뷰: `AuthorizationManager.check`를 이어서 이야기하면, enum을 사용하기에 적절한지 고려해보면 좋을 것 같아요.

물론 현재는 granted 조건이 true와 false밖에 없는 상황이긴 하지만 확장성을 고려해본다면 interface로 구성하는 것이 맞지 않을까요? 권한이 true인지, false인지 담아둘 수도 있지만 어떤 권한인지도 보유할 상황이 존재하지 않을까요?
@@ -0,0 +1,36 @@
+package nextstep.security.authorization.matcher;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.HttpMethod;
+
+import java.util.regex.Pattern;
+
+public class MvcRequestMatcher implements RequestMatcher {
+    private final HttpMethod method;
+    private final Pattern pattern;
+
+    public MvcRequestMatcher(HttpMethod method, String pattern) {
+        this.method = method;
+        this.pattern = compile(pattern);
+    }
+
+    private Pattern compile(String regex) {
+        if (regex == null || regex.isBlank()) {
+            return null;
+        }
+        return Pattern.compile(regex);
+    }
+
+    @Override
+    public boolean matches(HttpServletRequest request) {
+        return matchMethod(request) && matchPattern(request);
+    }
+
+    private boolean matchMethod(HttpServletRequest request) {
+        return method == null || method.name().equals(request.getMethod());
+    }
+
+    private boolean matchPattern(HttpServletRequest request) {
+        return pattern == null || pattern.matcher(request.getRequestURI()).matches();
+    }
+}

리뷰: ```suggestion
        if (regex == null || regex.isBlank()) {
            return null;
        }
        return Pattern.compile(regex);
```
개인적으로는 삼항연산자보다는 if문으로 early return 해주는 것이 한줄 한줄을 읽을 때 컨텍스트를 외워두지 않아도 되어서 읽기 편한 코드로 만들어준다고 생각합니다 :)
@@ -0,0 +1,34 @@
+package nextstep.security.authorization.manager;
+
+import nextstep.security.authentication.Authentication;
+import nextstep.security.authorization.Secured;
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.lang.reflect.Method;
+
+public class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
+    private SecuredAuthorizationManager() {}
+
+    public static SecuredAuthorizationManager getInstance() {
+        return SingletonHolder.INSTANCE;
+    }
+
+    @Override
+    public AuthorizationResult authorize(Authentication authentication, MethodInvocation target) {
+        return AuthorizationDecision.of(hasAuthority(authentication, target.getMethod()));
+    }
+
+    private boolean hasAuthority(Authentication authentication, Method method) {
+        if (!method.isAnnotationPresent(Secured.class)) {
+            return false;
+        }
+        return authentication != null
+                && authentication.isAuthenticated()
+                && authentication.getAuthorities()
+                .contains(method.getAnnotation(Secured.class).value());
+    }
+
+    private static final class SingletonHolder {
+        private static final SecuredAuthorizationManager INSTANCE = new SecuredAuthorizationManager();
+    }
+}

리뷰: `@Secured` 어노테이션이 없는 메소드를 타는 경우에는 인가 값이 true인 `AuthorizationDecision`을 반환하는 것이 맞을지 고민해보면 좋을 것 같아요.

`AuthorizationDecision`이 true인 객체를 반환한다는 것은 "인가"되었다는 것인데 `@Secured`어노테이션이 없는 것은 인가의 프로세스가 필요없지 않나요?
@@ -0,0 +1,58 @@
+package nextstep.security.authorization.manager;
+
+import jakarta.servlet.http.HttpServletRequest;
+import nextstep.security.authentication.Authentication;
+import nextstep.security.authorization.matcher.RequestMatcherEntry;
+
+import java.util.List;
+
+public class RequestAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
+    private final List<RequestMatcherEntry<AuthorizationManager<HttpServletRequest>>> entries;
+    private final RequestMatcherEntry<AuthorizationManager<HttpServletRequest>> defaultEntry;
+
+    public RequestAuthorizationManager(
+            List<RequestMatcherEntry<AuthorizationManager<HttpServletRequest>>> entries,
+            RequestMatcherEntry<AuthorizationManager<HttpServletRequest>> defaultEntry
+    ) {
+        this.entries = entries;
+        this.defaultEntry = defaultEntry;
+    }
+
+    @Override
+    public AuthorizationResult authorize(Authentication authentication, HttpServletRequest target) {
+        if (noneMatch(target)) {
+            return AuthorizationDecision.of(
+                    check(authentication, target, defaultEntry)
+            );
+        }
+        return AuthorizationDecision.of(
+                allMatch(authentication, target)
+        );
+    }
+
+    private boolean noneMatch(HttpServletRequest request) {
+        for (var entry : entries) {
+            if (entry.requestMatcher().matches(request)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean allMatch(Authentication authentication, HttpServletRequest request) {
+        for (var entry : entries) {
+            if (!check(authentication, request, entry)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean check(
+            Authentication authentication, HttpServletRequest request,
+            RequestMatcherEntry<AuthorizationManager<HttpServletRequest>> matcherEntry
+    ) {
+        return !matcherEntry.requestMatcher().matches(request)
+                || matcherEntry.entry().authorize(authentication, request).isGranted();
+    }
+}

리뷰: spring security에서는 성능의 문제로 stream문을 지양합니다.

https://github.com/spring-projects/spring-security/issues/7154
@@ -0,0 +1,22 @@
+package nextstep.security.authorization.manager;
+
+public enum AuthorizationDecision implements AuthorizationResult {
+    GRANTED(true), NOT_GRANTED(false);
+
+    private final boolean granted;
+
+    AuthorizationDecision(boolean granted) {
+        this.granted = granted;
+    }
+
+    public static AuthorizationDecision of(boolean granted) {
+        if (granted) {
+            return GRANTED;
+        }
+        return NOT_GRANTED;
+    }
+
+    public boolean isGranted() {
+        return this.granted;
+    }
+}

리뷰: Spring Security 의 [AuthorizationDecision](https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/authorization/AuthorizationDecision.java) 코드를 보았을 때, boolean granted 멤버를 가진 Value Objet 라고 생각을 해버렸네요.
어차피 true 와 false 밖에 없고 값객체로 만들거라면 enum 으로 만들어야지! 하고 생각했습니다.

하지만 말씀주신 대로 [AuthorizationResult](https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/authorization/AuthorizationResult.java) 라는 별도 인터페이스가 존재하는 군요.

[AuthorizationManager.check](https://github.com/spring-projects/spring-security/blob/27cb1154f25c74a6532dbd222ead9da53e880020/core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java#L57) 는 AuthorizationDecision 이 리턴타입이지만,
[AuthorizationManager.authorize](https://github.com/spring-projects/spring-security/blob/27cb1154f25c74a6532dbd222ead9da53e880020/core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java#L68) 는 AuthorizationResult 인터페이스인 것으로 보아서,
말씀하신대로의 확장성을 AuthorizationManager 가 고려하고 있는 상황이지 않을까하는 추측을 하게 되었습니다.

AuthorizationManager.check 가 deprecated 된 이유도, 리턴타입이 인터페이스가 아닌 AuthorizationDecision 라서 확장성이 떨어지기에 check 대신 authorize 를 사용하도록 권고하고 있나 라는 생각이 들었고요.

단, AuthorizationResult 인터페이스도 결국 isGranted() boolean 값을 반환하는 메서드 한개만을 가지고 있기에,
true, false 외에 권한을 확장가능한지에 대해서는 상상하기가 쉽지 않네요.

AuthorizationDecision 에 Authority 나, Role 과 같은 역할을 추가해서 확장하기를 바라신건가요?
제가 "어떤 권한인지 보유할 상황"에 피드백을 제대로 이해했는지 헷갈려서 여쭈어봅니다.


```java
public interface AuthorizationResult extends Serializable {
	boolean isGranted();
}
```