Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/397.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 自定义过滤器在WebMvcTest中突然中断_Java_Spring_Spring Boot_Spring Mvc_Spring Security - Fatal编程技术网

Java 自定义过滤器在WebMvcTest中突然中断

Java 自定义过滤器在WebMvcTest中突然中断,java,spring,spring-boot,spring-mvc,spring-security,Java,Spring,Spring Boot,Spring Mvc,Spring Security,我正在应用程序中实现JWT auth。一切正常,但当我运行已经存在的@WebMvcTests并检查返回的状态代码时,它们都以“实际:403”失败 这是我当前的测试套件之一: @WebMvcTest(controllers = UserController.class) @ContextConfiguration(classes = {JwtServiceImpl.class}) // custom filter dependency class UserControllerTest {

我正在应用程序中实现JWT auth。一切正常,但当我运行已经存在的@WebMvcTests并检查返回的状态代码时,它们都以“实际:403”失败

这是我当前的测试套件之一:

@WebMvcTest(controllers = UserController.class)
@ContextConfiguration(classes = {JwtServiceImpl.class}) // custom filter dependency
class UserControllerTest {

    /**
     * Mocked bean because it's a dependency of the SecurityConfiguration
     */
    @MockBean
    private UserDetailsService userDetailsService;

    @MockBean
    private UserService userService;

    @Autowired
    private ObjectMapper jsonMapper;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void create_should_return_registered_user_when_request_is_valid() throws Exception {
        // given
        final String EMAIL = "test@test.com";
        final String PASSWORD = "test_password";
        final UserDto userDto = buildDto(EMAIL, PASSWORD);
        final User expectedUser = buildUser(EMAIL, PASSWORD);

        // when
        when(userService.registerUser(userDto)).thenReturn(expectedUser);

        // then
        MvcResult response = mockMvc.perform(post(UserAPI.BASE_URL)
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonMapper.writeValueAsString(userDto)))
                .andExpect(status().isCreated())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andReturn();

        String responseBodyJson = response.getResponse().getContentAsString();
        User responseUser = jsonMapper.readValue(responseBodyJson, User.class);

        assertThat(responseUser.getId(), is(equalTo(expectedUser.getId())));
        assertThat(responseUser.getEmail(), is(equalTo(expectedUser.getEmail())));
        assertThat(responseUser.getPassword(), is(nullValue()));

        verify(userService, times(1)).registerUser(userDto);
        verifyNoMoreInteractions(userService);
    }

    ...

}
这是我的自定义过滤器:

@Slf4j
@Component
@RequiredArgsConstructor
public class AuthorizationFilter extends OncePerRequestFilter {

    public static final String AUTHORIZATION_HEADER_KEY = "Authorization";
    public static final String AUTHORIZATION_HEADER_PREFIX = "Bearer ";

    private final JwtService jwtService;
    private final UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String header = request.getHeader(AUTHORIZATION_HEADER_KEY);

        if (hasText(header) && header.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
            String jwt = header.substring(AUTHORIZATION_HEADER_PREFIX.length());
            Authentication establishedPrincipal = SecurityContextHolder.getContext().getAuthentication();

            if (!jwtService.isTokenExpired(jwt) && establishedPrincipal == null) {
                try {

                    String username = jwtService.extractUsername(jwt);

                    try {

                        UserDetails principal = userDetailsService.loadUserByUsername(username);

                        setRequestSession(request, principal);

                    } catch (UsernameNotFoundException exception) {
                        log.warn("Could not find user: {} extracted from jwt: {}", username, jwt);
                    }

                } catch (ExpiredJwtException exception) {
                    log.warn("Request to parse expired JWT: {} failed: {}", jwt, exception.getMessage());
                } catch (UnsupportedJwtException exception) {
                    log.warn("Request to parse unsupported JWT: {} failed: {}", jwt, exception.getMessage());
                } catch (MalformedJwtException exception) {
                    log.warn("Request to parse invalid JWT: {} failed: {}", jwt, exception.getMessage());
                } catch (SignatureException exception) {
                    log.warn("Request to parse JWT with invalid signature: {} failed: {}", jwt, exception.getMessage());
                } catch (IllegalArgumentException exception) {
                    log.warn("Request to parse empty or null JWT: {} failed: {}", jwt, exception.getMessage());
                }
            }
        }

        filterChain.doFilter(request, response);
    }

    private void setRequestSession(HttpServletRequest request, UserDetails principal) {
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(principal,
                null, principal.getAuthorities());

        authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

        SecurityContextHolder.getContext().setAuthentication(authToken);
    }

}
显然,MVC将自动选择过滤器。如果我从过滤器中删除@Component,它将不再被Spring上下文拾取,测试将全部通过

所以问题显然是过滤器,但我已经在doFilter的第一行设置了一个断点,它没有被击中

也许我应该自己配置MockMvc,而不是自动连接它?但是怎么做呢

以下是测试运行的完整输出:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /api/v1/users
       Parameters = {}
          Headers = [Content-Type:"application/json", Content-Length:"52"]
             Body = <no character encoding set>
    Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@62735b13}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []


java.lang.AssertionError: Status expected:<400> but was:<403>
Expected :400
Actual   :403
<Click to see difference>


    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
    at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:627)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196)
    at com.example.ordersapi.user.controller.UserControllerTest.create_should_return_bad_request_when_request_has_invalid_email(UserControllerTest.java:111)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

    ...
特别注意该行
2020-01-16 18:57:07.199调试30809---[main]o.s.security.web.csrf.CsrfFilter:为找到无效的csrf令牌http://localhost/api/v1/users
!我在配置中禁用了csrf,并且它也被拾取,因为我在测试中专门为它连接了一个依赖项(否则上下文初始化失败)

安全配置如下所示:

@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${spring.h2.console.enabled:false}")
    private boolean h2ConsoleEnabled;

    private final UserDetailsService userDetailsService;
    // private final AuthorizationFilter authorizationFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        if (h2ConsoleEnabled) {
            http.authorizeRequests()
                .antMatchers("/h2-console", "/h2-console/**").permitAll()
                .and()
                .headers().frameOptions().sameOrigin();
        }

        http.cors().and().csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(unauthorizedHandler())
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers(AuthenticationAPI.BASE_URL + "/**").permitAll()
                .antMatchers(ProductAPI.BASE_URL + "/**").permitAll()
                .antMatchers(UserAPI.BASE_URL + "/**").permitAll()
            .anyRequest().authenticated();

        // http.addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    private AuthenticationEntryPoint unauthorizedHandler() {
        return (request, response, e) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }

    /**
     * We need to override this method in order to add the @Bean annotation because Spring doesn't create an AuthenticationManager bean by default anymore.
     * Without this we can't wire AuthenticationManager in other beans.
     * @return AuthenticationManager bean
     * @throws Exception on unsuccessful bean creation
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}
我所期望的是:测试保持绿色,因为它们甚至没有针对受保护的路由。它应该对该控制器透明


实际情况:Spring上下文自动拾取的自定义过滤器正在破坏测试。如果我将其注销为Springbean(remove@Component),一切都会恢复正常。

仅供参考,spring正在使用其默认的安全配置,这就是事情变得糟糕的原因

我不确定启用和禁用我的自定义过滤器到底会产生什么影响,以及为什么它只是在那里使测试失败。无论如何,要解决这个问题,我必须使用@Import来导入我自己的安全配置。然后我不得不嘲笑它的依赖性

我创建了一个助手类,这样就不会污染实际的控制器测试:

@Import(SecurityConfiguration.class)
public abstract class SecurityEnabledSetup {

    /**
     * Mocked bean because it's a dependency of the SecurityConfiguration
     */
    @MockBean
    protected UserDetailsService userDetailsService;

    /**
     * Mocked bean because it's a dependency of the SecurityConfiguration
     */
    @MockBean
    protected JwtService jwtService;

}
我的最后一堂课是我希望是:

@WebMvcTest(controllers = UserController.class)
class UserControllerTest extends SecurityEnabledSetup {

    @MockBean
    private UserService userService;

    @Autowired
    private ObjectMapper jsonMapper;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void create_should_return_registered_user_when_request_is_valid() throws Exception {
        // given
        final String EMAIL = "test@test.com";
        final String PASSWORD = "test_password";
        final UserDto userDto = buildDto(EMAIL, PASSWORD);
        final User expectedUser = buildUser(EMAIL, PASSWORD);

        // when
        when(userService.registerUser(userDto)).thenReturn(expectedUser);

        // then
        MvcResult response = mockMvc.perform(post(UserAPI.BASE_URL)
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonMapper.writeValueAsString(userDto)))
                .andExpect(status().isCreated())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andReturn();

        String responseBodyJson = response.getResponse().getContentAsString();
        User responseUser = jsonMapper.readValue(responseBodyJson, User.class);

        assertThat(responseUser.getId(), is(equalTo(expectedUser.getId())));
        assertThat(responseUser.getEmail(), is(equalTo(expectedUser.getEmail())));
        assertThat(responseUser.getPassword(), is(nullValue()));

        verify(userService, times(1)).registerUser(userDto);
        verifyNoMoreInteractions(userService);
    }

    @Test
    void create_should_return_conflict_when_request_valid_but_email_in_use() throws Exception {
        // given
        final String EMAIL = "test@test.com";
        final String PASSWORD = "test_password";
        final UserDto userDto = buildDto(EMAIL, PASSWORD);

        // when
        when(userService.registerUser(userDto)).thenThrow(new EmailAlreadyInUseException(EMAIL));

        // then
        mockMvc.perform(post(UserAPI.BASE_URL)
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonMapper.writeValueAsString(userDto)))
                .andExpect(status().isConflict());

        verify(userService, times(1)).registerUser(userDto);
        verifyNoMoreInteractions(userService);
    }

    @Test
    void create_should_return_bad_request_when_request_has_invalid_email() throws Exception {
        // given
        final String BAD_EMAIL = "test_test.com";
        final String PASSWORD = "test_password";
        final UserDto userDto = buildDto(BAD_EMAIL, PASSWORD);

        // when

        // then
        mockMvc.perform(post(UserAPI.BASE_URL)
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonMapper.writeValueAsString(userDto)))
                .andExpect(status().isBadRequest());

        verifyNoInteractions(userService);
    }

    @Test
    void create_should_return_bad_request_when_request_has_invalid_password() throws Exception {
        // given
        final String EMAIL = "test@test.com";
        final String BAD_PASSWORD = "";
        final UserDto userDto = buildDto(EMAIL, BAD_PASSWORD);

        // when

        // then
        mockMvc.perform(post(UserAPI.BASE_URL)
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonMapper.writeValueAsString(userDto)))
                .andExpect(status().isBadRequest());

        verifyNoInteractions(userService);
    }

    @Test
    void create_should_return_bad_request_when_request_is_missing_email() throws Exception {
        // given
        final String PASSWORD = "test_password";
        final UserDto userDto = buildDto(null, PASSWORD);

        // when

        // then
        mockMvc.perform(post(UserAPI.BASE_URL)
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonMapper.writeValueAsString(userDto)))
                .andExpect(status().isBadRequest());

        verifyNoInteractions(userService);
    }

    @Test
    void create_should_return_bad_request_when_request_is_missing_password() throws Exception {
        // given
        final String EMAIL = "test@test.com";
        final UserDto userDto = buildDto(EMAIL, null);

        // when

        // then
        mockMvc.perform(post(UserAPI.BASE_URL)
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonMapper.writeValueAsString(userDto)))
                .andExpect(status().isBadRequest());

        verifyNoInteractions(userService);
    }

    private UserDto buildDto(String email, String password) {
        UserDto userDto = new UserDto();
        userDto.setEmail(email);
        userDto.setPassword(password);
        return userDto;
    }

    private User buildUser(String email, String password){
        User user = new User();
        user.setId(1);
        user.setEmail(email);
        user.setPassword(password);
        return user;
    }

}
我已经在我的安全配置中启用了过滤器,并且一切正常:

@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${spring.h2.console.enabled:false}")
    private boolean h2ConsoleEnabled;

    private final UserDetailsService userDetailsService;
    private final AuthorizationFilter authorizationFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        if (h2ConsoleEnabled) {
            http.authorizeRequests()
                .antMatchers("/h2-console", "/h2-console/**").permitAll()
                .and()
                .headers().frameOptions().sameOrigin();
        }

        http.cors().and().csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(unauthorizedHandler())
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers(AuthenticationAPI.BASE_URL + "/**").permitAll()
                .antMatchers(ProductAPI.BASE_URL + "/**").permitAll()
                .antMatchers(UserAPI.BASE_URL + "/**").permitAll()
            .anyRequest().authenticated();

        http.addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    private AuthenticationEntryPoint unauthorizedHandler() {
        return (request, response, e) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }

    /**
     * We need to override this method in order to add the @Bean annotation because Spring doesn't create an AuthenticationManager bean by default anymore.
     * Without this we can't wire AuthenticationManager in other beans.
     * @return AuthenticationManager bean
     * @throws Exception on unsuccessful bean creation
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${spring.h2.console.enabled:false}")
    private boolean h2ConsoleEnabled;

    private final UserDetailsService userDetailsService;
    private final AuthorizationFilter authorizationFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        if (h2ConsoleEnabled) {
            http.authorizeRequests()
                .antMatchers("/h2-console", "/h2-console/**").permitAll()
                .and()
                .headers().frameOptions().sameOrigin();
        }

        http.cors().and().csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(unauthorizedHandler())
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers(AuthenticationAPI.BASE_URL + "/**").permitAll()
                .antMatchers(ProductAPI.BASE_URL + "/**").permitAll()
                .antMatchers(UserAPI.BASE_URL + "/**").permitAll()
            .anyRequest().authenticated();

        http.addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    private AuthenticationEntryPoint unauthorizedHandler() {
        return (request, response, e) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }

    /**
     * We need to override this method in order to add the @Bean annotation because Spring doesn't create an AuthenticationManager bean by default anymore.
     * Without this we can't wire AuthenticationManager in other beans.
     * @return AuthenticationManager bean
     * @throws Exception on unsuccessful bean creation
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}