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.
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.