Unable to fix CORS issue with Spring Boot and Angular

Chiranjeev Jain :

I am new to Spring Boot and Angular and struggling with the CORS issue.

I want to implement a global CORS filter and tried implementing CorsFilter, WebMvcConfigurer and also tried with WebMvcConfigurerAdapter class but none of the methods seem to be working. I know I'm missing something but can't figure out what. If someone could please find a solution in my code, it would be great help.

I also have implemented Basic Auth and added that code if that's any help.

Here's my code.

Angular API Call

api.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor(private httpClient: HttpClient) { }

  userLogin(username: String, password: String) {
    let postBody = {
      "username" : username,
      "password" : password
    };
    let httpOptions = {
      headers : new HttpHeaders({
        "Content-Type" : "application/json",
        "Authorization" : "Basic " + btoa('myUsername:myPassword')
      })
    }
    return this.httpClient.post("http://localhost:8080/userLogin", postBody, httpOptions);
  }

}

Here's my API code.

MainApp.java

@SpringBootApplication
public class MainApp extends SpringBootServletInitializer {
    public static void main(String... str) {
        SpringApplication.run(MainApp.class, str);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(MainApp.class);
    }

}

Config.java

@Configuration
public class Config {

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
        configuration.setAllowedMethods(Arrays.asList("POST"));
        source.registerCorsConfiguration("/**", configuration);
        return new CorsFilter(source);
    }
}

NotebookController.java

@RestController
public class NotebookController {

    @Autowired
    IUsersRepo usersRepo;
    @Autowired
    INotesRepo notesRepo;

    @PostMapping("userLogin")
    @ResponseBody
    public ResponseEntity<String> userLogin(@RequestParam("username") String username, @RequestParam("password") String password) {
        ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter();
        List<Users> usersList = usersRepo.findByUsernameEqualsAndPasswordEquals(username, password);
        boolean userVerified = usersList.size() > 0 ? true : false;
        HashMap<String, Boolean> outputMap = new HashMap<>();
        outputMap.put("authenticated", userVerified);
        String outputJson = "";
        try {
            outputJson = objectWriter.writeValueAsString(outputMap);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        outputJson = outputJson.toString();
        ResponseEntity<String> response = new ResponseEntity<String>(outputJson, HttpStatus.OK);
        return response;
    }
}

Authentication.java

@Configuration
@EnableWebSecurity
public class Authentication extends WebSecurityConfigurerAdapter {

    @Autowired
    AuthenticationEntryPoint authEntryPoint;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.cors().and().csrf().disable();

        //Authorise all incoming requests
        http.authorizeRequests().and().httpBasic();
        http.authorizeRequests().anyRequest().authenticated();

        //Use AuthenticationEntryPoint to authorise username/password
        http.httpBasic().authenticationEntryPoint(authEntryPoint);
    }

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
        configuration.setAllowedMethods(Arrays.asList("POST"));
        source.registerCorsConfiguration("/**", configuration);
        return new CorsFilter(source);
    }

    @Bean
    public BCryptPasswordEncoder getEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        String username = "myUsername";
        String password = "myPassword";

        InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> mngConfig = authenticationManagerBuilder.inMemoryAuthentication();

        mngConfig.withUser(username).password(password).roles("USER");
    }

}

AuthEntryPoint.java

@Component
public class AuthEntryPoint extends BasicAuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        super.commence(request, response, authException);
        response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName());
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        PrintWriter writer = response.getWriter();
        writer.println("HTTP Status 401: Unauthorised | " + authException.getMessage());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        setRealmName("Online Notebook");
        super.afterPropertiesSet();
    }

}

Please see the browser console as well.

https://imgur.com/a/hHceJDw

Ananthapadmanabhan :

Since you are using spring security , you could configure cors along with spring security like below so that the pre-flight check request goes through spring security.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
            //other configurations that you want
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource()
    {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        //or any domain that you want to restrict to 
        configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept","Authorization"));
   configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        //Add the method support as you like 
        UrlBasedCorsConfigurationSource source = new     UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

The issue that you might be facing is that spring security is not letting through the pre-flight checkup requests and failing them. For more analysis , please share your browser logs or errors from the request .

From the browser error it can be seen that the header "Authorization" that you have added to the request is not allowed by the cors configuration as you have specified only the headers "Origin", "Content-Type", "Accept" in your cors configuration. Add the "Authorization" header as well to cors configuration and the issue should be fixed.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=477886&siteId=1