GeekCoder
GeekCoder

Reputation: 140

Spring Boot Security module gives 403 error when called by using axios from react but works fine in postman

I have a spring boot project that uses spring security with JWT token. This works fine in POSTMAN but it gives a 403 error when using react axios. Below is the code used

SecurityConfig.java

    package com.cg.practice.EmployeeCRUD.config;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.BeanIds;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.crypto.password.NoOpPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.web.cors.CorsConfiguration;
    
    import com.cg.practice.EmployeeCRUD.Service.CustomUserService;
    import com.cg.practice.EmployeeCRUD.filter.EmployeeCRUDFilter;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        CustomUserService userService;
        
        @Autowired
        EmployeeCRUDFilter employeeCRUDFilter;
        
        /*
         * @Autowired CorsFilter corsFilter;
         */
    
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // TODO Auto-generated method stub
            System.out.println("Hi1");
            auth.userDetailsService(userService);
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return NoOpPasswordEncoder.getInstance();
        }
    
        @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
            @Override
        protected void configure(HttpSecurity http)  {
            // TODO Auto-generated method stub
            System.out.println("Hi2");
              try { 
                  CorsConfiguration corsConfiguration = new CorsConfiguration();
                    corsConfiguration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
                    corsConfiguration.setAllowedOrigins(List.of("http://localhost:3000"));
                    corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PUT","OPTIONS","PATCH", "DELETE"));
                    corsConfiguration.setAllowCredentials(true);
                    corsConfiguration.setExposedHeaders(List.of("Authorization"));
                  
                  http
                 // .addFilterBefore(corsFilter, SessionManagementFilter.class) 
                  .csrf().disable().authorizeRequests().antMatchers("/authenticate/")
                  .permitAll().anyRequest().authenticated()
                  .and().exceptionHandling().and().sessionManagement()
                  .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                  .and().cors().configurationSource(request -> corsConfiguration);
    ;
          http.addFilterBefore(employeeCRUDFilter, UsernamePasswordAuthenticationFilter.class);
                  } 
              catch (Exception e) 
              { 
                      // TODO: handle exception 
                      e.printStackTrace(); 
                      }
                  }
             
        }

EmployeeCRUDFilter.java

package com.cg.practice.EmployeeCRUD.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.cg.practice.EmployeeCRUD.Service.CustomUserService;
import com.cg.practice.EmployeeCRUD.util.JwtUtil;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;

@Component
public class EmployeeCRUDFilter extends OncePerRequestFilter {

    @Autowired
    JwtUtil jwtUtil;
    @Autowired
    CustomUserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            {
        // TODO Auto-generated method stub
        String authorizationHeader = request.getHeader("Authorization");

        String token = null;
        String userName = null;
try
{
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            token = authorizationHeader.substring(7);
            userName = jwtUtil.extractUsername(token);
        }

        if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = userService.loadUserByUsername(userName);

            if (jwtUtil.validateToken(token, userDetails)) {

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        filterChain.doFilter(request, response);
    }   
catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
}
            }
    }

CRUDController method

@PostMapping("/authenticate")
    
    public String generateToken(@RequestBody AuthRequest authRequest) throws Exception
    {

        try {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                authRequest.getUserName(),authRequest.getPassword())        
                        );
        }
        catch (Exception e) {
            return "Invalid User/Password";
        }
        return jwtUtil.generateToken(authRequest.getUserName());
    }

React Authservice.js

import http from '../Mycomponent/http-common';

class AuthService{

    authenticate(){
        return http.post("/authenticate", {"userName":"Dip","password":"password@123"}
        )
        .then(response => {
            console.log(response.data)
            if (response.data.accessToken) {
              localStorage.setItem("user", JSON.stringify(response.data));
            }
            return response.data;
          })
          .catch(error =>{
            console.log("Error :"+error);
          });
    }
}
export default new AuthService();

http-common.js

import axios from 'axios';

export default axios.create({
  baseURL: "http://localhost:8080/CRUD",
  headers: {
    "Content-type": "application/json"
      }
});

This gives a 403 error in browser

Failed to load resource: the server responded with a status of 403 ()
AuthService.js:16 Error :Error: Request failed with status code 403
:8080/CRUD/getAll:1          Failed to load resource: the server responded with a status of 403 ()
createError.js:16 Uncaught (in promise) Error: Request failed with status code 403
    at createError (createError.js:16:1)
    at settle (settle.js:17:1)
    at XMLHttpRequest.onloadend (xhr.js:66:1)

Can anyone please help. I am stuck on it for a long time. Am I missing something? I am new to react.

Works fine in PostMAN

Upvotes: 1

Views: 5860

Answers (2)

Michal Trojanowski
Michal Trojanowski

Reputation: 12342

I think here lies the problem:

http
    .csrf().disable().authorizeRequests().antMatchers("/authenticate/")
    .permitAll().anyRequest().authenticated()
    .and().exceptionHandling().and().sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and().cors().configurationSource(request -> corsConfiguration);

You tell spring to permitAll requests to the /authenticate/ endpoint, and require authentication for all the other requests. But from the frontend you're making a request to /CRUD/authenticate/. That's why you get 403, because this path must be authenticated - meaning that the request must already have the Authorization header. I think it should work if you change the first line to this:

.csrf().disable().authorizeRequests().antMatchers("/CRUD/authenticate/")

Upvotes: 0

Jason
Jason

Reputation: 11

Seems like you have missed Authorization in the header when you setup your axios instance

Upvotes: 1

Related Questions