Search code examples
javaspringspring-bootspring-securityjwt

Successful creation of UsernamePasswordAuthenticationToken but I still get a 401 error in Java Spring with JWT


I trying to create API with JSON Web Token. Unfortunately once configured, I am having trouble executing queries. Because I get an 401 Unauthorized error - despite successfully generating and sending the token in the heade.

How can I try to figure out what is wrong?

This is my JwtAuthorizationFilter, after send a query with token logger.info(auth.getName()); returns correct username.

public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserDetailsService userDetailsService, String secret) {
        super(authenticationManager);
        this.userDetailsService = userDetailsService;
        this.secret = secret;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(request);
        if (authenticationToken == null) {
            logger.warn("Authentication token is null");
            filterChain.doFilter(request, response);
            return;
        }
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(TOKEN_HEADER);
        if (token != null && token.startsWith(TOKEN_PREFIX)) {
            logger.info(token);
            String userName = JWT.require(Algorithm.HMAC256(secret)).build().verify(token.replace(TOKEN_PREFIX, "")).getSubject();
            if (userName != null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), null,
                        userDetails.getAuthorities());
                logger.info(auth.getName());
                return auth;
            }
        }
        return null;
    }

This is security config:

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.cors().and().csrf(AbstractHttpConfigurer::disable) //
                .authorizeHttpRequests(auth -> {
                    auth.requestMatchers(HttpMethod.GET, "/**").permitAll() //
                            .requestMatchers(HttpMethod.PUT, "/login").permitAll() //
                            .requestMatchers(HttpMethod.PUT, "admin/**").authenticated(); //
                }).authenticationManager(authenticationManager(authenticationConfiguration)) //
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) //
                .addFilter(authenticationFilter()) //
                .addFilter(new JwtAuthorizationFilter(authenticationManager(authenticationConfiguration), userDetailsManager(), secret)) //
                .exceptionHandling() //
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) //
                .and().headers().frameOptions().disable().and().build();
    }

    public JsonObjectAuthenticationFilter authenticationFilter() throws Exception {
        JsonObjectAuthenticationFilter authenticationFilter = new JsonObjectAuthenticationFilter(objectMapper);
        authenticationFilter.setAuthenticationSuccessHandler(successHandler);
        authenticationFilter.setAuthenticationFailureHandler(failureHandler);
        authenticationFilter.setAuthenticationManager(authenticationManager(authenticationConfiguration));
        return authenticationFilter;
    }

@Bean
    AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsManager userDetailsManager() {
        return new JdbcUserDetailsManager(dataSource);
    }

I tried to change the configuration of securityFilterChain, but change the order of filters, etc. But unfortunately, I can not deal with this problem.


Solution

  • Ok, I found the solution by changing this part:

    .authorizeHttpRequests(auth -> {
                        auth.requestMatchers(HttpMethod.GET, "/**").permitAll() //
                                .requestMatchers(HttpMethod.PUT, "/login").permitAll() //
                                .requestMatchers(HttpMethod.PUT, "admin/**").authenticated(); //
                    })
    

    To this:

    .authorizeHttpRequests(auth -> {
                        auth.requestMatchers(HttpMethod.GET, "/**").permitAll() //
                                .requestMatchers(HttpMethod.POST, "/login").permitAll() //
                                .anyRequest().authenticated(); //
                    }) //
    

    Now it works as I would like, but I still don't understand why it didn't work properly:

    requestMatchers(HttpMethod.PUT, "admin/**").authenticated()