'Getting 404 error in spring boot even though method is logging statements

My app uses Angular on the frontend and Spring Boot (w basic authentication & Spring security) on the backend. I'm using a proxy for api requests to my backend as follows:

{
  "/api": {
    "target": "http://localhost:8080",
    "secure": false,
    "loglevel": "debug"
  }
}

I have a method in Angular to query my backend with user credentials set in the header:

  login(credentials: User) {
    console.log("test");
    const headers = new HttpHeaders(credentials ? {
      authorization: 'Basic ' + btoa(credentials.username + ':' + credentials.password)
    } : {});

    this.http.get('api/users/user', {headers: headers}).subscribe(
      response => {
        console.log(response);
        // @ts-ignore
        if (response['name']) {
          this.authenticated = true;
          // @ts-ignore
          this.authenticatedUserName = response['username'];
        } else {
          console.log("test3");
          this.authenticated = false;
        }
      });
    console.log("test4");
  }

Where the backend request mapping looks like this:

@RestController
@RequestMapping("api/users/")
public class UserProfileController {
  private final static Logger LOGGER = 
  Logger.getLogger(UserProfileController.class.getName());

    @Autowired
    private PlayerService playerService;

    @RequestMapping("user")
    @ResponseBody
    public ResponseEntity<Principal> user(Principal user) {
        LOGGER.info("User is " + user);
        return new ResponseEntity<Principal>(user, HttpStatus.OK);
    }

When I enter incorrect user credentials on my Angular login form, Spring Security intercepts and I get a 403 error as expected. However, when I enter the correct credentials, I see the logger message on the backend ("User is ...") alongside a 404 error-- GET http://localhost:4200/api/users/user 404 (Not Found). The backend returns the correct user, but the frontend doesn't ever enter the subscribe method block. The request is clearly hitting my backend, so I'm not sure what's going on, and would appreciate any insight on the matter.

For reference, here are my security configurations:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final static Logger LOGGER = Logger.getLogger(UserDetailsServiceImpl.class.getName());
    

    private UserDetailsServiceImpl userDetailsService;
    private final CustomAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    public SecurityConfig(CustomAuthenticationEntryPoint authenticationEntryPoint, UserDetailsServiceImpl userDetailsService) {
        this.authenticationEntryPoint = authenticationEntryPoint;
        this.userDetailsService = userDetailsService;
    }

    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();

        provider.setPasswordEncoder(passwordEncoder());
        provider.setUserDetailsService(userDetailsService);
        return provider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                    .csrf().disable()
                    .authorizeRequests()
                    .antMatchers("/api/**").permitAll()
                    .anyRequest().authenticated().and().
                httpBasic().authenticationEntryPoint(authenticationEntryPoint);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
    }
}



Solution 1:[1]

You have not defined the request mapping correctly. best practice is to have controller level annotation like:

@RequestMapping("/users/*")
public class UsersController {

     @RequestMapping("user")
     @ResponseBody
     public ResponseEntity<Principal> user(Principal user) {
     LOGGER.info("User is " + user);
      return new ResponseEntity<Principal>(user, HttpStatus.OK);
    }
}

Solution 2:[2]

By default, spring doesn't allow any url to access data for security, your angular app runs on localhost4200 and sprint boot on 8080, so to allow angular to connect the sprint boot API, add @CrossOrigin in your controller/Resource class

   @CrossOrigin(origins="http://localhost:4200")
@RestController
public class TodoController {
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Juliyanage Silva
Solution 2 sarthak sethi