diff --git a/src/main/java/nextstep/app/config/AuthConfig.java b/src/main/java/nextstep/app/config/AuthConfig.java index 7e4e0f1..a9014c3 100644 --- a/src/main/java/nextstep/app/config/AuthConfig.java +++ b/src/main/java/nextstep/app/config/AuthConfig.java @@ -1,12 +1,16 @@ package nextstep.app.config; +import java.util.List; +import javax.servlet.Filter; +import nextstep.security.access.matcher.AnyRequestMatcher; import nextstep.security.access.matcher.MvcRequestMatcher; import nextstep.security.authentication.AuthenticationManager; import nextstep.security.authentication.BasicAuthenticationFilter; import nextstep.security.authentication.UsernamePasswordAuthenticationFilter; import nextstep.security.authentication.UsernamePasswordAuthenticationProvider; import nextstep.security.authorization.AuthorizationFilter; -import nextstep.security.authorization.RoleManager; +import nextstep.security.authorization.PreAuthorizationFilter; +import nextstep.security.config.AuthorizeRequestMatcherRegistry; import nextstep.security.config.DefaultSecurityFilterChain; import nextstep.security.config.FilterChainProxy; import nextstep.security.config.SecurityFilterChain; @@ -17,6 +21,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.web.filter.DelegatingFilterProxy; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @@ -34,49 +39,32 @@ public DelegatingFilterProxy securityFilterChainProxy() { } @Bean - public FilterChainProxy filterChainProxy( - SecurityFilterChain loginSecurityFilterChain, - SecurityFilterChain membersSecurityFilterChain - ) { - return new FilterChainProxy( - loginSecurityFilterChain, - membersSecurityFilterChain - ); + public FilterChainProxy filterChainProxy(SecurityFilterChain securityFilterChain) { + return new FilterChainProxy(securityFilterChain); } @Bean - public SecurityFilterChain loginSecurityFilterChain( + public SecurityFilterChain securityFilterChain( AuthenticationManager authenticationManager, SecurityContextRepository securityContextRepository ) { - return new DefaultSecurityFilterChain( - new MvcRequestMatcher( - HttpMethod.POST, - "/login" - ), + final List filters = List.of( new UsernamePasswordAuthenticationFilter( authenticationManager, securityContextRepository - ) - ); - } - - @Bean - public SecurityFilterChain membersSecurityFilterChain( - AuthenticationManager authenticationManager, - SecurityContextRepository securityContextRepository - ) { - return new DefaultSecurityFilterChain( - new MvcRequestMatcher( - HttpMethod.GET, - "/members" ), new BasicAuthenticationFilter( authenticationManager, securityContextRepository ), - new AuthorizationFilter(securityContextRepository, new RoleManager("ADMIN")) + new PreAuthorizationFilter(securityContextRepository), + new AuthorizationFilter( + new AuthorizeRequestMatcherRegistry() + .matcher(new MvcRequestMatcher(HttpMethod.GET, "/members")).hasAuthority("ADMIN") + .matcher(new MvcRequestMatcher(HttpMethod.GET, "/members/me")).authenticated() + ) ); + return new DefaultSecurityFilterChain(AnyRequestMatcher.INSTANCE, filters); } @Bean @@ -89,4 +77,8 @@ public AuthenticationManager authenticationManager() { return new AuthenticationManager(new UsernamePasswordAuthenticationProvider(userDetailsService)); } + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new LoginUserArgumentResolver()); + } } diff --git a/src/main/java/nextstep/app/config/LoginUserArgumentResolver.java b/src/main/java/nextstep/app/config/LoginUserArgumentResolver.java new file mode 100644 index 0000000..0ea9918 --- /dev/null +++ b/src/main/java/nextstep/app/config/LoginUserArgumentResolver.java @@ -0,0 +1,30 @@ +package nextstep.app.config; + +import nextstep.app.ui.dto.LoginUser; +import nextstep.security.authentication.Authentication; +import nextstep.security.context.SecurityContextHolder; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().equals(LoginUser.class); + } + + @Override + public LoginUser resolveArgument( + MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory + ) { + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + final String email = authentication.getPrincipal().toString(); + return new LoginUser(email); + } +} diff --git a/src/main/java/nextstep/app/ui/MemberController.java b/src/main/java/nextstep/app/ui/MemberController.java index fce08d7..a527474 100644 --- a/src/main/java/nextstep/app/ui/MemberController.java +++ b/src/main/java/nextstep/app/ui/MemberController.java @@ -1,16 +1,17 @@ package nextstep.app.ui; -import nextstep.security.authentication.Authentication; -import nextstep.security.context.SecurityContextHolder; +import java.util.List; import nextstep.app.domain.Member; import nextstep.app.domain.MemberRepository; +import nextstep.app.ui.dto.LoginUser; +import nextstep.security.exception.AuthenticationException; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.List; - @RestController +@RequestMapping("/members") public class MemberController { private final MemberRepository memberRepository; @@ -19,10 +20,16 @@ public MemberController(MemberRepository memberRepository) { this.memberRepository = memberRepository; } - @GetMapping("/members") + @GetMapping public ResponseEntity> list() { List members = memberRepository.findAll(); return ResponseEntity.ok(members); } + @GetMapping("/me") + public ResponseEntity me(LoginUser loginUser) { + final Member member = memberRepository.findByEmail(loginUser.getEmail()) + .orElseThrow(() -> new AuthenticationException()); + return ResponseEntity.ok(member); + } } diff --git a/src/main/java/nextstep/app/ui/dto/LoginUser.java b/src/main/java/nextstep/app/ui/dto/LoginUser.java new file mode 100644 index 0000000..acbecba --- /dev/null +++ b/src/main/java/nextstep/app/ui/dto/LoginUser.java @@ -0,0 +1,20 @@ +package nextstep.app.ui.dto; + +public class LoginUser { + private final String email; + + public LoginUser(String email) { + this.email = email; + } + + public String getEmail() { + return email; + } + + @Override + public String toString() { + return "LoginUser{" + + "email='" + email + '\'' + + '}'; + } +} diff --git a/src/main/java/nextstep/security/access/matcher/MvcRequestMatcher.java b/src/main/java/nextstep/security/access/matcher/MvcRequestMatcher.java index abfb3d4..eca7322 100644 --- a/src/main/java/nextstep/security/access/matcher/MvcRequestMatcher.java +++ b/src/main/java/nextstep/security/access/matcher/MvcRequestMatcher.java @@ -28,6 +28,6 @@ public boolean matches(HttpServletRequest request) { return false; } - return request.getRequestURI().contains(pattern); + return request.getRequestURI().equals(pattern); } } diff --git a/src/main/java/nextstep/security/authorization/AuthorizationFilter.java b/src/main/java/nextstep/security/authorization/AuthorizationFilter.java index 98065a6..33d09c9 100644 --- a/src/main/java/nextstep/security/authorization/AuthorizationFilter.java +++ b/src/main/java/nextstep/security/authorization/AuthorizationFilter.java @@ -1,26 +1,28 @@ package nextstep.security.authorization; import java.io.IOException; +import java.util.Optional; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import nextstep.security.context.SecurityContext; -import nextstep.security.context.SecurityContextRepository; +import nextstep.security.authentication.Authentication; +import nextstep.security.authorization.manager.AuthorizationManager; +import nextstep.security.config.AuthorizeRequestMatcherRegistry; +import nextstep.security.context.SecurityContextHolder; +import nextstep.security.exception.AuthenticationException; import nextstep.security.exception.AuthorizationException; import org.springframework.http.HttpStatus; import org.springframework.web.filter.GenericFilterBean; public class AuthorizationFilter extends GenericFilterBean { - private final SecurityContextRepository securityContextRepository; - private final RoleManager roleManager; + private final AuthorizeRequestMatcherRegistry authorizeRequestMatcherRegistry; - public AuthorizationFilter(SecurityContextRepository securityContextRepository, RoleManager roleManager) { - this.securityContextRepository = securityContextRepository; - this.roleManager = roleManager; + public AuthorizationFilter(AuthorizeRequestMatcherRegistry authorizeRequestMatcherRegistry) { + this.authorizeRequestMatcherRegistry = authorizeRequestMatcherRegistry; } @Override @@ -30,12 +32,32 @@ public void doFilter( FilterChain chain ) throws IOException, ServletException { try { - final SecurityContext context = securityContextRepository.loadContext((HttpServletRequest) request); - if (context.getAuthentication().getAuthorities().stream().noneMatch(roleManager::hasRole)) { + final Authentication authentication = Optional.ofNullable( + SecurityContextHolder + .getContext() + .getAuthentication() + ).orElseThrow(AuthenticationException::new); + + final AuthorizationManager authorizationManager = authorizeRequestMatcherRegistry.getAuthorizationManager((HttpServletRequest) request); + + if (authorizationManager == null) { + return; + } + + if (!authorizationManager.check(authentication)) { throw new AuthorizationException(); } + } catch (AuthenticationException e) { + ((HttpServletResponse) response).sendError( + HttpStatus.UNAUTHORIZED.value(), + HttpStatus.UNAUTHORIZED.getReasonPhrase() + ); + return; } catch (AuthorizationException e) { - ((HttpServletResponse) response).sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase()); + ((HttpServletResponse) response).sendError( + HttpStatus.FORBIDDEN.value(), + HttpStatus.FORBIDDEN.getReasonPhrase() + ); return; } diff --git a/src/main/java/nextstep/security/authorization/PreAuthorizationFilter.java b/src/main/java/nextstep/security/authorization/PreAuthorizationFilter.java new file mode 100644 index 0000000..42af3d0 --- /dev/null +++ b/src/main/java/nextstep/security/authorization/PreAuthorizationFilter.java @@ -0,0 +1,38 @@ +package nextstep.security.authorization; + +import java.io.IOException; +import java.util.Optional; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import nextstep.security.context.SecurityContext; +import nextstep.security.context.SecurityContextHolder; +import nextstep.security.context.SecurityContextRepository; +import org.springframework.web.filter.GenericFilterBean; + +public class PreAuthorizationFilter extends GenericFilterBean { + + private final SecurityContextRepository securityContextRepository; + + public PreAuthorizationFilter(SecurityContextRepository securityContextRepository) { + this.securityContextRepository = securityContextRepository; + } + + @Override + public void doFilter( + ServletRequest request, + ServletResponse response, + FilterChain chain + ) throws IOException, ServletException { + final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + + final Optional securityContext = Optional.ofNullable( + securityContextRepository.loadContext(httpServletRequest) + ); + securityContext.ifPresent(it -> SecurityContextHolder.setContext(it)); + + chain.doFilter(request, response); + } +} diff --git a/src/main/java/nextstep/security/authorization/RoleManager.java b/src/main/java/nextstep/security/authorization/RoleManager.java deleted file mode 100644 index 884fb4b..0000000 --- a/src/main/java/nextstep/security/authorization/RoleManager.java +++ /dev/null @@ -1,20 +0,0 @@ -package nextstep.security.authorization; - -import java.util.Set; - -public class RoleManager { - - private final Set roles; - - public RoleManager(Set roles) { - this.roles = roles; - } - - public RoleManager(String... roles) { - this(Set.of(roles)); - } - - public boolean hasRole(String role) { - return roles.contains(role); - } -} diff --git a/src/main/java/nextstep/security/authorization/manager/AuthenticatedAuthorizationManager.java b/src/main/java/nextstep/security/authorization/manager/AuthenticatedAuthorizationManager.java new file mode 100644 index 0000000..09b768f --- /dev/null +++ b/src/main/java/nextstep/security/authorization/manager/AuthenticatedAuthorizationManager.java @@ -0,0 +1,11 @@ +package nextstep.security.authorization.manager; + +import nextstep.security.authentication.Authentication; + +public class AuthenticatedAuthorizationManager implements AuthorizationManager { + + @Override + public boolean check(Authentication authentication) { + return authentication.isAuthenticated(); + } +} diff --git a/src/main/java/nextstep/security/authorization/manager/AuthorityAuthorizationManager.java b/src/main/java/nextstep/security/authorization/manager/AuthorityAuthorizationManager.java new file mode 100644 index 0000000..453c22a --- /dev/null +++ b/src/main/java/nextstep/security/authorization/manager/AuthorityAuthorizationManager.java @@ -0,0 +1,23 @@ +package nextstep.security.authorization.manager; + +import java.util.Set; +import nextstep.security.authentication.Authentication; + +public class AuthorityAuthorizationManager implements AuthorizationManager { + + private final Set authorities; + + public AuthorityAuthorizationManager(Set authorities) { + this.authorities = authorities; + } + + public AuthorityAuthorizationManager(String... authorities) { + this(Set.of(authorities)); + } + + @Override + public boolean check(Authentication authentication) { + return authentication.getAuthorities().stream() + .anyMatch(authorities::contains); + } +} diff --git a/src/main/java/nextstep/security/authorization/manager/AuthorizationManager.java b/src/main/java/nextstep/security/authorization/manager/AuthorizationManager.java new file mode 100644 index 0000000..2c03ffd --- /dev/null +++ b/src/main/java/nextstep/security/authorization/manager/AuthorizationManager.java @@ -0,0 +1,8 @@ +package nextstep.security.authorization.manager; + +import nextstep.security.authentication.Authentication; + +public interface AuthorizationManager { + + boolean check(Authentication authentication); +} diff --git a/src/main/java/nextstep/security/authorization/manager/DenyAllAuthorizationManager.java b/src/main/java/nextstep/security/authorization/manager/DenyAllAuthorizationManager.java new file mode 100644 index 0000000..71bd73c --- /dev/null +++ b/src/main/java/nextstep/security/authorization/manager/DenyAllAuthorizationManager.java @@ -0,0 +1,11 @@ +package nextstep.security.authorization.manager; + +import nextstep.security.authentication.Authentication; + +public class DenyAllAuthorizationManager implements AuthorizationManager { + + @Override + public boolean check(Authentication authentication) { + return false; + } +} diff --git a/src/main/java/nextstep/security/authorization/manager/PermitAllAuthorizationManager.java b/src/main/java/nextstep/security/authorization/manager/PermitAllAuthorizationManager.java new file mode 100644 index 0000000..3f7b118 --- /dev/null +++ b/src/main/java/nextstep/security/authorization/manager/PermitAllAuthorizationManager.java @@ -0,0 +1,11 @@ +package nextstep.security.authorization.manager; + +import nextstep.security.authentication.Authentication; + +public class PermitAllAuthorizationManager implements AuthorizationManager { + + @Override + public boolean check(Authentication authentication) { + return true; + } +} diff --git a/src/main/java/nextstep/security/config/AuthorizeRequestMatcherRegistry.java b/src/main/java/nextstep/security/config/AuthorizeRequestMatcherRegistry.java new file mode 100644 index 0000000..1ceeb52 --- /dev/null +++ b/src/main/java/nextstep/security/config/AuthorizeRequestMatcherRegistry.java @@ -0,0 +1,63 @@ +package nextstep.security.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import nextstep.security.access.matcher.RequestMatcher; +import nextstep.security.authorization.manager.AuthenticatedAuthorizationManager; +import nextstep.security.authorization.manager.AuthorizationManager; +import nextstep.security.authorization.manager.AuthorityAuthorizationManager; +import nextstep.security.authorization.manager.DenyAllAuthorizationManager; +import nextstep.security.authorization.manager.PermitAllAuthorizationManager; + +public class AuthorizeRequestMatcherRegistry { + private final Map mappings = new HashMap<>(); + + public AuthorizedUrl matcher(RequestMatcher requestMatcher) { + return new AuthorizedUrl(requestMatcher); + } + + AuthorizeRequestMatcherRegistry addMapping(RequestMatcher requestMatcher, AuthorizationManager authorizationManager) { + mappings.put(requestMatcher, authorizationManager); + return this; + } + + public AuthorizationManager getAuthorizationManager(HttpServletRequest request) { + for (var entry : mappings.entrySet()) { + if (entry.getKey().matches(request)) { + return entry.getValue(); + } + } + + return null; + } + + public class AuthorizedUrl { + private final RequestMatcher requestMatcher; + + public AuthorizedUrl(RequestMatcher requestMatcher) { + this.requestMatcher = requestMatcher; + } + + public AuthorizeRequestMatcherRegistry permitAll() { + return access(new PermitAllAuthorizationManager()); + } + + public AuthorizeRequestMatcherRegistry denyAll() { + return access(new DenyAllAuthorizationManager()); + } + + public AuthorizeRequestMatcherRegistry hasAuthority(String... authorities) { + return access(new AuthorityAuthorizationManager(authorities)); + } + + public AuthorizeRequestMatcherRegistry authenticated() { + return access(new AuthenticatedAuthorizationManager()); + } + + private AuthorizeRequestMatcherRegistry access(AuthorizationManager authorizationManager) { + return addMapping(requestMatcher, authorizationManager); + } + } + +} diff --git a/src/main/java/nextstep/security/config/DefaultSecurityFilterChain.java b/src/main/java/nextstep/security/config/DefaultSecurityFilterChain.java index f27eabf..23abebd 100644 --- a/src/main/java/nextstep/security/config/DefaultSecurityFilterChain.java +++ b/src/main/java/nextstep/security/config/DefaultSecurityFilterChain.java @@ -1,11 +1,9 @@ package nextstep.security.config; - -import nextstep.security.access.matcher.RequestMatcher; - +import java.util.List; import javax.servlet.Filter; import javax.servlet.http.HttpServletRequest; -import java.util.List; +import nextstep.security.access.matcher.RequestMatcher; public class DefaultSecurityFilterChain implements SecurityFilterChain { diff --git a/src/main/java/nextstep/security/context/HttpSessionSecurityContextRepository.java b/src/main/java/nextstep/security/context/HttpSessionSecurityContextRepository.java index 45f3c56..5b8ab7e 100644 --- a/src/main/java/nextstep/security/context/HttpSessionSecurityContextRepository.java +++ b/src/main/java/nextstep/security/context/HttpSessionSecurityContextRepository.java @@ -5,7 +5,7 @@ import javax.servlet.http.HttpSession; public class HttpSessionSecurityContextRepository implements SecurityContextRepository { - public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT"; + private static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT"; @Override public SecurityContext loadContext(HttpServletRequest request) { diff --git a/src/main/java/nextstep/security/context/SecurityContextHolder.java b/src/main/java/nextstep/security/context/SecurityContextHolder.java index f295d33..4f83053 100644 --- a/src/main/java/nextstep/security/context/SecurityContextHolder.java +++ b/src/main/java/nextstep/security/context/SecurityContextHolder.java @@ -1,7 +1,7 @@ package nextstep.security.context; public class SecurityContextHolder { - public static final String SPRING_SECURITY_CONTEXT_KEY = "SECURITY_CONTEXT"; + public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT"; private static final ThreadLocal contextHolder; static { diff --git a/src/test/java/nextstep/app/MemberAcceptanceTest.java b/src/test/java/nextstep/app/MemberAcceptanceTest.java index 264733e..44755e9 100644 --- a/src/test/java/nextstep/app/MemberAcceptanceTest.java +++ b/src/test/java/nextstep/app/MemberAcceptanceTest.java @@ -57,6 +57,43 @@ void get_members_after_form_login_user() { assertThat(memberResponse.statusCode()).isEqualTo(HttpStatus.FORBIDDEN.value()); } + @DisplayName("인증된 사용자는 200 응답을 받고 members/me 조회가 가능하다.") + @Test + void get_me_after_form_login_user() { + final String loginEmail = USER.getEmail(); + final ExtractableResponse loginResponse = 로그인(loginEmail, USER.getPassword()); + + ExtractableResponse memberResponse = RestAssured.given().log().all() + .cookies(loginResponse.cookies()) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when() + .get("/members/me") + .then().log().all() + .extract(); + + assertAll( + () -> assertThat(memberResponse.statusCode()).isEqualTo(HttpStatus.OK.value()), + () -> { + Member me = memberResponse.as(Member.class); + assertThat(me.getEmail()).isEqualTo(loginEmail); + } + ); + } + + @DisplayName("인증되지 않은 사용자는 401 응답을 받고 members/me 조회가 불가능하다.") + @Test + void get_me_after_form_not_login_user() { + + ExtractableResponse memberResponse = RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when() + .get("/members/me") + .then().log().all() + .extract(); + + assertThat(memberResponse.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); + } + private ExtractableResponse 로그인(String username, String password) { return RestAssured.given().log().all() .formParams(Map.of( diff --git a/src/test/java/nextstep/security/config/FilterChainProxyTest.java b/src/test/java/nextstep/security/config/FilterChainProxyTest.java index 475d08d..863e6f1 100644 --- a/src/test/java/nextstep/security/config/FilterChainProxyTest.java +++ b/src/test/java/nextstep/security/config/FilterChainProxyTest.java @@ -1,20 +1,24 @@ package nextstep.security.config; -import nextstep.security.access.matcher.MvcRequestMatcher; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import nextstep.security.access.matcher.AnyRequestMatcher; import nextstep.security.fixture.MockFilterChain; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockHttpServletRequest; -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +class FilterChainProxyTest { -public class FilterChainProxyTest { private FilterChainProxy filterChainProxy; private TestFilter loginTestFilter; private TestFilter membersTestFilter; @@ -22,36 +26,37 @@ public class FilterChainProxyTest { @BeforeEach void setUp() { loginTestFilter = new TestFilter(); - SecurityFilterChain loginFilterChain = new DefaultSecurityFilterChain( - new MvcRequestMatcher(HttpMethod.POST, "/login"), - List.of(loginTestFilter) - ); - membersTestFilter = new TestFilter(); - SecurityFilterChain membersFilterChain = new DefaultSecurityFilterChain( - new MvcRequestMatcher(HttpMethod.GET, "/members"), - List.of(membersTestFilter) - ); - filterChainProxy = new FilterChainProxy(List.of(loginFilterChain, membersFilterChain)); + filterChainProxy = new FilterChainProxy( + new DefaultSecurityFilterChain( + AnyRequestMatcher.INSTANCE, + loginTestFilter, + membersTestFilter + ) + ); } @Test void login() throws ServletException, IOException { HttpServletRequest request = new MockHttpServletRequest(HttpMethod.POST.name(), "/login"); - filterChainProxy.doFilter(request, null, null); + filterChainProxy.doFilter(request, null, new MockFilterChain()); - assertThat(loginTestFilter.count).isEqualTo(1); - assertThat(membersTestFilter.count).isEqualTo(0); + assertAll( + () -> assertThat(loginTestFilter.count).isEqualTo(1), + () -> assertThat(membersTestFilter.count).isEqualTo(1) + ); } @Test void members() throws ServletException, IOException { HttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.name(), "/members"); - filterChainProxy.doFilter(request, null, null); + filterChainProxy.doFilter(request, null, new MockFilterChain()); - assertThat(loginTestFilter.count).isEqualTo(0); - assertThat(membersTestFilter.count).isEqualTo(1); + assertAll( + () -> assertThat(loginTestFilter.count).isEqualTo(1), + () -> assertThat(membersTestFilter.count).isEqualTo(1) + ); } @Test @@ -59,17 +64,24 @@ void none() throws ServletException, IOException { HttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.name(), "/test"); filterChainProxy.doFilter(request, null, new MockFilterChain()); - - assertThat(loginTestFilter.count).isEqualTo(0); - assertThat(membersTestFilter.count).isEqualTo(0); + assertAll( + () -> assertThat(loginTestFilter.count).isEqualTo(1), + () -> assertThat(membersTestFilter.count).isEqualTo(1) + ); } private static class TestFilter implements Filter { + private int count = 0; @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + public void doFilter( + ServletRequest request, + ServletResponse response, + FilterChain chain + ) throws ServletException, IOException { count++; + chain.doFilter(request, response); } } }