I'm new to spring boot and I implemented some basic REST apis using spring boot. And when I tried to call those api using react I've got an error calling some CORS policies. Then I found a solution to that problem, And all my api end point working correctly except the login api call. I get the same error calling:
Here is my web security java class.
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private final UserService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL)
.permitAll().anyRequest().authenticated().and().cors().and().addFilter(getAuthenticationFilter())
.addFilter(new AuthorizationFilter(authenticationManager())).sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
public AuthenticationFilter getAuthenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl("/users/login");
return filter;
}
}
And here is my example controller class.
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
UserService userService;
#CrossOrigin
#GetMapping(path = "/{id}",
produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public UserRest getUser(#PathVariable String id) {
UserRest returnValue = new UserRest();
UserDto userDto = userService.getUserByUserId(id);
BeanUtils.copyProperties(userDto, returnValue);
return returnValue;
}
#CrossOrigin
#PostMapping(
consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE },
produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public UserRest createUser(#RequestBody UserDetailsRequestModel userDetails) {
UserRest returnValue = new UserRest();
UserDto userDto = new UserDto();
BeanUtils.copyProperties(userDetails, userDto);
UserDto createUser = userService.createUser(userDto);
BeanUtils.copyProperties(createUser, returnValue);
return returnValue;
}
}
I'm stuck with this problem and I really need some help. I've tried some answers given in stackoverflow as similar to my problem. But that didn't solve my problem. Mt spring boot version is 2.1.4
You are facing the issue because,you have allowed cors on your backend from port 8080, but your react is up in port 3000 on local.So , springboot will not accept the request as it is coming from a different port address.
Method 1 :
You could get away with this by using the annotation like :
#CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
Since you are using springboot , you can also use the following global config to define which all domains have access to your backend.
#Configuration
public class MyConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("PUT", "DELETE", "GET", "POST") //or allow all as you like
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
};
}
}
and if you are still having issues , it might be because the spring security is not adding the CORS headers like Access-Control-Allow-Origin in your response header. In that case , you can define a custom CORS filter and add it to the spring security so that the CORS response headers are set for all spring security endpoints . You can create a filter bean like :
public class CorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request= (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", true);
response.setHeader("Access-Control-Max-Age", 180);
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
}
}
Method 2:
since you are using spring security you could also add the CORS configuration along with spring security like :
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#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.setAllowedMethods(Arrays.asList("GET","POST"));
//Add the method support as you like
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Spring security will take up the CORS configuration even if you provide it like in method 1 as per my knowledge.But , in spring documentation they have provided this way of registering CORS with spring security as well.
In your console log that you have attached it is said that the pre flight check failed because of http status returned is not OK .So , can you try registering cors like method 2 and have a filter added so that the headers are added correctly in response. Springboot is blocking the pre flight check when the login page is accessed. You need to get your cors setup properly with the spring security to allow login page to be accessed from a dfferent port.
Confirm that your login page /login mapping have CORS allowed
Official Doc
Reference Doc
Try this,
#Configuration
public class WebConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*");
}
}
Update
you could also do the below,
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Collections.singletonList("*"));
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> filterRegistration = new FilterRegistrationBean<>(new CorsFilter(source));
filterRegistration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filterRegistration;
}
Related
I have created two web applications - client and service apps.The interaction between client and service apps goes fine when they are deployed in same Tomcat instance.
But when the apps are deployed into seperate Tomcat instances (different machines), I get the below error when request to sent service app.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401
My Client application uses JQuery, HTML5 and Bootstrap.
AJAX call is made to service as shown below:
var auth = "Basic " + btoa({usname} + ":" + {password});
var service_url = {serviceAppDomainName}/services;
if($("#registrationForm").valid()){
var formData = JSON.stringify(getFormData(registrationForm));
$.ajax({
url: service_url+action,
dataType: 'json',
async: false,
type: 'POST',
headers:{
"Authorization":auth
},
contentType: 'application/json',
data: formData,
success: function(data){
//success code
},
error: function( jqXhr, textStatus, errorThrown ){
alert( errorThrown );
});
}
My service application uses Spring MVC, Spring Data JPA and Spring Security.
I have included CorsConfiguration class as shown below:
CORSConfig.java:
#Configuration
#EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*");
}
}
SecurityConfig.java:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#ComponentScan(basePackages = "com.services", scopedProxy = ScopedProxyMode.INTERFACES)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("authenticationService")
private UserDetailsService userDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated();
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
}
Spring Security dependencies:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
I am using Apache Tomcat server for deployment.
CORS' preflight request uses HTTP OPTIONS without credentials, see Cross-Origin Resource Sharing:
Otherwise, make a preflight request. Fetch the request URL from origin source origin using referrer source as override referrer source with the manual redirect flag and the block cookies flag set, using the method OPTIONS, and with the following additional constraints:
Include an Access-Control-Request-Method header with as header field value the request method (even when that is a simple method).
If author request headers is not empty include an Access-Control-Request-Headers header with as header field value a comma-separated list of the header field names from author request headers in lexicographical order, each converted to ASCII lowercase (even when one or more are a simple header).
Exclude the author request headers.
Exclude user credentials.
Exclude the request entity body.
You have to allow anonymous access for HTTP OPTIONS.
Spring Security 3
Your modified (and simplified) code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
}
You still need your CORS configuration (probably with some additional values):
#Configuration
#EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*");
}
}
Spring Security 4
Since Spring Security 4.2.0 you can use the built-in support, see Spring Security Reference:
19. CORS
Spring Framework provides first class support for CORS. CORS must be processed before Spring Security because the pre-flight request will not contain any cookies (i.e. the JSESSIONID). If the request does not contain any cookies and Spring Security is first, the request will determine the user is not authenticated (since there are no cookies in the request) and reject it.
The easiest way to ensure that CORS is handled first is to use the CorsFilter. Users can integrate the CorsFilter with Spring Security by providing a CorsConfigurationSource using the following:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors().and()
...
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Spring Security 5/6
For Spring Security 5/6 see Spring Security Reference:
CORS
Spring Framework provides first class support for CORS. CORS must be processed before Spring Security, because the pre-flight request does not contain any cookies (that is, the JSESSIONID). If the request does not contain any cookies and Spring Security is first, the request determines that the user is not authenticated (since there are no cookies in the request) and rejects it.
The easiest way to ensure that CORS is handled first is to use the CorsFilter. Users can integrate the CorsFilter with Spring Security by providing a CorsConfigurationSource that uses the following:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors(withDefaults())
...
return http.build();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
With Spring Security 5/6 you don't need to configure CORS twice (Spring Security and Spring MVC):
If you use Spring MVC’s CORS support, you can omit specifying the CorsConfigurationSource and Spring Security uses the CORS configuration provided to Spring MVC:
Since Spring Security 4.1, this is the proper way to make Spring Security support CORS (also needed in Spring Boot 1.4/1.5):
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
}
and:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable();
http.cors();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(ImmutableList.of("*"));
configuration.setAllowedMethods(ImmutableList.of("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Do not do any of below, which are the wrong way to attempt solving the problem:
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
web.ignoring().antMatchers(HttpMethod.OPTIONS);
Reference: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
In my case, I have Resource Server with OAuth security enabled and any of above solutions didn't work. After some debugging and googling figured why.
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
Basically in this example Ordered.HIGHEST_PRECEDENCE is key!
https://github.com/spring-projects/spring-security-oauth/issues/938
Various pom dependencies add different kinds of filters and therefore we could have issues based on order.
Add the below configuration in the main application. It worked me in spring boot application 2.3.1
package com.example.restservicecors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#SpringBootApplication
public class RestServiceCorsApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceCorsApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
};
}
}
Reference source: https://spring.io/guides/gs/rest-service-cors/
Since none of this posted examples helped me, I've taken things in my own knowledge.
In this method:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
CorsConfiguration by default have allowed method: POST, HEAD, GET, so PUT, DELETE will not work. What I did is I created a new instance of CorsConfiguration and set allowed methods:
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
so now my method looks like:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", cors.applyPermitDefaultValues());
return source;
}
Of course, all other configuration is made by Spring documentation.
Try this:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
import java.util.List;
#Component
public class CorsFilterConfig {
public static final List<String> allowedOrigins = Arrays.asList("*");
#Bean
public FilterRegistrationBean<CorsFilter> initCorsFilter() {
// #formatter:off
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
config.addAllowedMethod("*");
config.setAllowedOrigins(allowedOrigins);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
// #formatter:on
}
}
You can easily add the #CrossOrigin annotation to allow all of them if you use UsernamePasswordAuthenticationFilter. And in the security configurations the http.cors().and(). This worked for me.
#CrossOrigin(origins = "*")
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManagerBean());
customAuthenticationFilter.setFilterProcessesUrl("/api/login");
http
.csrf().disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// We can ant match out paths to the corresponding roles --> we allow certain roles to access certain API's
http
.cors()
.and();
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**").permitAll();
...
This worked for: spring-boot-starter-parent 2.2.6.RELEASE
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
}
Change "*" to something meaningful in prod
This question already has answers here:
How to specify response headers to CORS?
(3 answers)
Angular 6 + Spring Boot: Error: "from origin 'http://localhost:4200' has been blocked by CORS policy"
(5 answers)
Closed 2 years ago.
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
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.
I created a REST API and uploaded it to Google Cloud. When I try to access from another domain, it gives the following error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://35.198.15.248:8080/api/clientes/pf/. (Reason: CORS request did not succeed).
Then I added the following class:
#Configuration
#EnableWebMvc
public class ConfiguracaoDeCors extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST, GET, HEAD, DELETE, UPDATE")
.allowedHeaders("Content-Type", "Authorization")
.allowCredentials(false)
.maxAge(32400);
}
}
And the error continues.
Could anyone help?
ADD:
Its working
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(PUBLICOS).permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Using the CrossOrigin annotation provided by spring might be helpful in your case:
import org.springframework.web.bind.annotation.CrossOrigin;
#CrossOrigin(origins = "http://localhost:9000")
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
If your configuring via GlobalConfiguration, the snippet below might be helpful as well.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}
You can use the following annotation in the controller class methods you want to allow cross-origin requests.
#CrossOrigin(origins = "*", methods = { RequestMethod.POST }, allowedHeaders = "*")
#RequestMapping(value = "/{order}/details", method = { RequestMethod.POST }, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public #ResponseBody Order getOrderDetail(#PathVariable Long order, HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
//...
}
In this example, all requests to /123456/details will allow cross origin for POST method. In your configuration, you're allowing CORS to all requests and that is not a good practice.
I've been having an issue with CORS and I have tried everything I could find on Stack Overflow and basically anything that I found on Google and have had no luck.
So I have user authentication on my backend and I have a login page on my frontend. I hooked up the login page with Axios so I could make a post request and tried to login but I kept getting errors like "Preflight request" so I fixed that then I started getting the "Post 403 Forbidden" error.
It appeared like this:
POST http://localhost:8080/api/v1/login/ 403 (Forbidden)
Even trying to login using Postman doesn't work so something is clearly wrong. Will be posting class files below
On my backend, I have a class called WebSecurityConfig which deals with all the CORS stuff:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS");
}
};
}
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*"); // TODO: lock down before deploying
config.addAllowedHeader("*");
config.addExposedHeader(HttpHeaders.AUTHORIZATION);
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http
.cors()
.and()
.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/h2/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/v1/login").permitAll()
.anyRequest().authenticated()
.and()
// We filter the api/login requests
.addFilterBefore(new JWTLoginFilter("/api/v1/login", authenticationManager()),
UsernamePasswordAuthenticationFilter.class);
// And filter other requests to check the presence of JWT in header
//.addFilterBefore(new JWTAuthenticationFilter(),
// UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Create a default account
auth.userDetailsService(userDetailsService);
// auth.inMemoryAuthentication()
// .withUser("admin")
// .password("password")
// .roles("ADMIN");
}
}
On our frontend which is written in VueJS and using Axios to make the call
<script>
import { mapActions } from 'vuex';
import { required, username, minLength } from 'vuelidate/lib/validators';
export default {
data() {
return {
form: {
username: '',
password: ''
},
e1: true,
response: ''
}
},
validations: {
form: {
username: {
required
},
password: {
required
}
}
},
methods: {
...mapActions({
setToken: 'setToken',
setUser: 'setUser'
}),
login() {
this.response = '';
let req = {
"username": this.form.username,
"password": this.form.password
};
this.$http.post('/api/v1/login/', req)
.then(response => {
if (response.status === 200) {
this.setToken(response.data.token);
this.setUser(response.data.user);
this.$router.push('/dashboard');
} else {
this.response = response.data.error.message;
}
}, error => {
console.log(error);
this.response = 'Unable to connect to server.';
});
}
}
}
</script>
So when I debugged via Chrome's tools (Network), I noticed that the OPTIONS request goes through as shown below:
Here is a picture of the POST error:
Here is another class which handles the OPTIONS request (JWTLoginFilter as referenced in the WebSecurityConfig):
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(
HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException, IOException, ServletException {
AccountCredentials creds = new ObjectMapper()
.readValue(req.getInputStream(), AccountCredentials.class);
if (CorsUtils.isPreFlightRequest(req)) {
res.setStatus(HttpServletResponse.SC_OK);
return null;
}
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
Collections.emptyList()
)
);
}
#Override
protected void successfulAuthentication(
HttpServletRequest req,
HttpServletResponse res, FilterChain chain,
Authentication auth) throws IOException, ServletException {
TokenAuthenticationService
.addAuthentication(res, auth.getName());
}
}
When you configure Axios, you can simply specify the header once and for all:
import axios from "axios";
const CSRF_TOKEN = document.cookie.match(new RegExp(`XSRF-TOKEN=([^;]+)`))[1];
const instance = axios.create({
headers: { "X-XSRF-TOKEN": CSRF_TOKEN }
});
export const AXIOS = instance;
Then (here I assume you use SpringBoot 2.0.0, while it should work also in SpringBoot 1.4.x onward) in your Spring Boot application you should add the following security configs.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// CSRF Token
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// you can chain other configs here
}
}
In this way, Spring will return the token as a cookie in the response (I assume you do a GET first) and you will read it in the AXIOS configuration file.
You should not disable CSRF as per Spring Security documentation except, few special cases. This code will put the CSRF header to VUE. I used vue-resource.
//This token is from Thymeleaf JS generation.
var csrftoken = [[${_csrf.token}]];
console.log('csrf - ' + csrftoken) ;
Vue.http.headers.common['X-CSRF-TOKEN'] = csrftoken;
Hope this helps.
Axios will, by default, handle the X-XSRF-TOKEN correctly.
So the only action is to configure the server, like JeanValjean explained:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// CSRF Token
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// you can chain other configs here
}
}
Axios will automatically send the correct token in the request headers, so there's no need to change the front-end.
I had the same kind of problem where a GET request was working, and yet a POST request was replied with status 403.
I found that for my case, it was because of the CSRF protection enabled by default.
A quick way to make sure of this case is to disable CSRF:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// …
#Override
protected void configure(HttpSecurity http) throws Exception {
// …
http.csrf().disable();
// …
}
// …
}
More information on Spring-Security website.
Mind that disabling CSRF isn't always the correct answer as it is there for security purpose.
I'm using spring security with OAuth2 (version: 4.0.4.RELEASE) and spring (verison: 4.3.1.RELEASE).
I'm developing frontend in Angular and I'm using grunt connect:dev (http://127.0.0.1:9000). When I trying to login by localhost address everything working fine but from other I'm getting error:
"XMLHttpRequest cannot load http://localhost:8084/oauth/token?client_id=MY_CLIENT_ID. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:9000' is therefore not allowed access. The response had HTTP status code 401."
I have configured mapping (Overrided public void addCorsMappings(CorsRegistry registry)) in WebMvcConfigurerAdapter (like below) but it still not working for http://127.0.0.1:9000.
registry.addMapping("/**")
.allowedOrigins("http://127.0.0.1:9000")
.allowedMethods("POST", "OPTIONS", "GET", "DELETE", "PUT")
.allowedHeaders("X-Requested-With,Origin,Content-Type,Accept,Authorization")
.allowCredentials(true).maxAge(3600);
Configuration based on: https://spring.io/guides/gs/rest-service-cors/
Please, point me the right directon to resolve this issue.
Hopefully, you found an answer long ago, but if not (and if anyone else finds this question searching as I was):
The issue is that Spring Security operates using filters and those filters generally have precedence over user defined filters, #CrossOrigin and similar annotations, etc.
What worked for me was to define the CORS filter as a bean with highest precedence, as suggested here.
#Configuration
public class MyConfiguration {
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://127.0.0.1:9000");
config.setAllowedMethods(Arrays.asList("POST", "OPTIONS", "GET", "DELETE", "PUT"));
config.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization"));
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
Sorry for long time response. I resolved the issue by configuring my CORS filter like below:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
private static final Logger LOGGER = LogManager.getLogger(CORSFilter.class.getName());
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
final String origin = ((HttpServletRequest) req).getHeader("Origin");
if (ofNullable(origin).isPresent() && origin.equals("http://127.0.0.1:9000")) {
LOGGER.info("CORSFilter run");
response.addHeader("Access-Control-Allow-Origin", "http://127.0.0.1:9000");
response.addHeader("Access-Control-Allow-Credentials", "true");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept,Authorization");
response.setStatus(200);
}
}
chain.doFilter(addNessesaryHeaders(request), response);
}
private MutableHttpServletRequest addNessesaryHeaders(final HttpServletRequest request) {
final MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(request);
mutableRequest.putHeader("Accept", "application/json");
mutableRequest.putHeader("Authorization", "Basic" + " bXVzaWNzY2hvb2w6");
return mutableRequest;
}
#Override
public void destroy() {
}
}
You can try something like that
#Configuration
public class CorsConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods(HttpMethod.OPTIONS.name(),
HttpMethod.PATCH.name(),
HttpMethod.PUT.name(),
HttpMethod.DELETE.name(),
HttpMethod.GET.name(),
HttpMethod.POST.name())
.maxAge(360);
}
};
}
}
Note: Spring version should be 4.2 or later
below worked for me.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
}