Spring API Cross Domain - reactjs

I got a tomcat server using Spring and a React website. I'm trying to fetch data, with postman it's ok but when I'm using my react frontend this is the error I got :
Access to fetch at
'http://127.0.0.1:8080/planningAccouplementWS/acquerirFemelles' from
origin 'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: It
does not have HTTP ok status.
On the frontend side, I'm calling this code :
static acquerirFemelles(femelles) {
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Content-Type", "application/json");
myHeaders.append("apikey", "sfj4-R5sdhffhs-fnhvSDFYT:DSRrfdj#fhqsm4zxwc-vhglxs15");
let obj = [];
femelles.forEach(femelle => {
obj.push({
"codePays": femelle.cheptel_pays,
"numeroNational": femelle.numero_national.substring(2),
"nom": femelle.bovin_nom
})
});
var raw = JSON.stringify({
"listeFemelles": obj
});
console.log(raw);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw
};
fetch("http://127.0.0.1:8080/planningAccouplementWS/acquerirFemelles", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
And on my server side :
WebConfig.java
public class WebConfig implements WebMvcConfigurer{
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH")
.allowedOrigins("http://locahlost:3000")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
#Order(1)
#PropertySource(value = "classpath:/security.properties")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Value("${yourapp.http.auth-token-header-name}")
private String principalRequestHeader;
#Value("${yourapp.http.auth-token}")
private String principalRequestValue;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
System.out.println("Cors configure");
APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!principalRequestValue.equals(principal))
{
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
}
});
httpSecurity.
antMatcher("/**").
csrf().disable().cors().and().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilter(filter).authorizeRequests().anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl());
}
#Bean
CorsConfigurationSource corsConfigurationSource()
{
System.out.println("Cors corsConfigurationSource");
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedOrigin("http://localhost:3000");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
CorsFilter filter = new CorsFilter(source);
return source;
}
}
And finaly my GET method
#CrossOrigin(origins = "*", allowedHeaders = "*")
#PostMapping("/acquerirFemelles")
public DonneesElevages acquerirFemelle(#RequestBody DonneesElevages donneesElevage) throws MetierException {
System.out.println("Acquerir Femelles");
DonneesElevages de = new DonneesElevages();
FemelleMetier femelleMetier = new FemelleMetier();
CoefficientMetier coefMetier = new CoefficientMetier();
HashMap<String, ParametreIndex> listeParametreIndex = coefMetier.rechercherParametresIndex();
de.setListeFemelles(femelleMetier.rechercheFemelles(donneesElevage.getListeFemelles(), listeParametreIndex));
return de;
}

You have the #CrossOrigin annotation on your mapping method, put this annotation on to your controller class.
if you want to do it the simple way, you can just use #CrossOrigin annotation up on your controller classes.
But anyway, be cautious that this will let through all requests no matter from which url or port they're coming from.

Related

Postman request to Spring endpoint works, but not from React Axios Request

As the title says I cannot get my post request to my Spring Controller to return a response, but from postman it returns the JWT token which I am trying to get no problem.
I have tried to implement CORS in my Spring Backend and heres how I did with a Config class:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
config.setAllowCredentials(true);
config.addAllowedHeader("*"); // Allow any header
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/auth/**", config);
return source;
}
I also enabled #Cors within my Controller class:
#RestController
#EnableMethodSecurity
#CrossOrigin
#RequestMapping("/api/auth")
#RequiredArgsConstructor
public class UserController {
private final ProviderManager authenticationManager;
#Autowired
UserService userService;
#Autowired
UserRepository repository;
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
JwtService jwtService;
#Autowired
DetailService userDetailService;
#PostMapping(value = "/register", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> handleRegistration(#RequestBody UserRegistrationForm userRegistrationForm,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
UserDTO userDTO = new UserDTO();
userDTO.setEmail(userRegistrationForm.getEmail());
userDTO.setPassword(passwordEncoder.encode(userRegistrationForm.getPassword()));
userDTO.setFirstName(userRegistrationForm.getFirstName());
userDTO.setLastName(userRegistrationForm.getLastName());
userDTO.setRoleName(Set.of("ROLE_USER"));
userDTO.setUsername(userRegistrationForm.getUsername());
User users = new User();
users.setUsername(userDTO.getUsername());
users.setEmail(userDTO.getEmail());
users.setPassword(userDTO.getPassword());
users.setFirstName(userDTO.getFirstName());
users.setLastName(userDTO.getLastName());
users.setRoleName(userDTO.getRoleName());
try {
User user = userService.saveUser(users);
if(user != null) {
response.setStatus(HttpServletResponse.SC_ACCEPTED);
String jwtToken = jwtService.generateToken(userDetailService.loadUserByUsername(user.getUsername()));
return ResponseEntity.ok(jwtToken);
}
} catch (Exception e) {
if (repository.findByUsername(users.getUsername()).isPresent()
&& repository.findByEmail(users.getEmail()).isPresent()) {
throw new UserException(String.format("User already exists with the username: %s and the email: %s",
users.getUsername(), users.getEmail()));
} else if (repository.findByUsername(users.getUsername()).isPresent()) {
throw new UserException(String.format("User already exists with the username: %s", users.getUsername()));
} else if (repository.findByEmail(users.getEmail()).isPresent()) {
throw new UserException(String.format("User already exists with the email: %s", users.getEmail()));
}
}
response.sendRedirect("/login?registrationSuccess=true");
return (ResponseEntity<?>) ResponseEntity.status(HttpServletResponse.SC_ACCEPTED);
}
Finally my SecurityFilterChain
which I tried to follow: https://docs.spring.io/spring-security/reference/reactive/integrations/cors.html
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
.csrf()
.disable()
.cors()
.and()
.authorizeHttpRequests()
.requestMatchers("/api/auth/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authProvider)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
Sample postman request:
When I try from Axios with the following code
const submitRegistration = (event) => {
console.log(registrationData);
axios.post("http://localhost:8080/api/auth/register", registrationData)
.then(response =>{
if(response.status === 200){
console.log(response);
window.location.replace("/login?registration=success");
}else{
console.error("Error registering: ", response.data)
}
}).catch(error =>{
console.log(registrationData);
console.error(error);
window.location = '/error';
});
}
I get:
I then after seeing the You need to enable javascript portion tried adding
"proxy": "http://localhost:3000",
To no avail.
Any thoughts would be appreciated :)

CORS policy error - spotify api with Spring boot + React

I am trying to write simple app with spotify api using Spring Boot and React.
In spring boot site i have a good working controller:
#RestController
#CrossOrigin()
public class SpotifyTopArtistClient {
#GetMapping("/artist")
public SpotifyArtist getTopArtist(OAuth2Authentication details){
String jwt = ((OAuth2AuthenticationDetails)details.getDetails()).getTokenValue();
RestTemplate restTemplate = new RestTemplate();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Authorization","Bearer "+jwt);
HttpEntity httpEntity = new HttpEntity(httpHeaders);
ResponseEntity<SpotifyArtist>
exchange=restTemplate.exchange("https://api.spotify.com/v1/me/top/artists?
time_range=medium_term&limit=1&offset=0", HttpMethod.GET,httpEntity,SpotifyArtist.class);
return exchange.getBody();
}
}
In class with the main method I have the bean:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS")
.allowCredentials(true);
}
};
}
and my security class:
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/test").authenticated();
http.authorizeRequests().antMatchers("/artist").authenticated();
http.authorizeRequests().antMatchers("/login").authenticated();
http.authorizeRequests().antMatchers("/login/*").authenticated();
http.cors().and().csrf().disable();
}
}
When i'm testing endpoint http://localhost:8080/artist in browser and postman - it works as i expected.
On react side i have code:
componentDidMount(){
fetch('http://localhost:8080/artist')
.then(response => response.json())
.then(data=>{
console.log(data)
})
}
and when i'm trying to run this i see policy-CORS error:
Access to fetch at 'https://accounts.spotify.com/authorize?client_id=8c8db951b0614847b5faf36b300fcb07&redirect_uri=http://localhost:8080/login&response_type=code&scope=user-read-private%20user-read-email%20user-top-read&state=vpHQLG' (redirected from 'http://localhost:8080/artist') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Why addnotation CrossOrigin above SpotifyTopArtistClient class doesn't work this?
Maybe someone have a better experience with spotify api and could help me?
Please
Try the following:
#RestController
public class SpotifyTopArtistClient {
(...)
}
Now update the WebMvcConfigurer:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS")
.allowCredentials(true);
}
};
}

Getting CORS policy exception: No 'Access-Control-Allow-Origin' header is present on the requested resource. ReactJS/SpringBoot

Well, this is my Exception I get in the browser:
Access to XMLHttpRequest at 'http://localhost:8080/home/data' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The Backend is a Spring Security Application and the Frontend is a ReactJS application. On the frontend I'd like to send a GET request to the backend with login information. This is the code in the frontend:
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return 'Basic ' + hash;
}
export default class Login extends Component {
getData() {
$.ajax({
type: 'GET',
url: 'http://localhost:8080/home/data',
dataType: 'json',
//whatever you need
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', make_base_auth('fabian.graml#gmail.com', 'password'));
},
success: function(response){
console.log(response)
}
});
}
The getData() function is executed when you click on a text on the page.
As mentioned the Backend is a Spring Boot Application.
This is my SecurityConfiguration.java:
#Configuration
#EnableWebSecurity
#AllArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
private final CustomUserService customUserService;
#Bean
public BCryptPasswordEncoder encodePasswd() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.authorizeRequests()
.antMatchers("/home/**")
.authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(new BCryptPasswordEncoder());
provider.setUserDetailsService(customUserService);
return provider;
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.addAllowedHeader("Access-Control-Allow-Origin");
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
And this is my CorsConfiguration.java
#Configuration
public class CorsConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer(){
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("**")
.allowedOrigins("**");
}
};
}
}
My question: Does somone know how to fix this exception or to turn off CORS? Would be great if someone can help me with my problem.
I think you need to add #CrossOrigin(origins = "*", maxAge = 3600) annotation in your controller

How to submit jwt token to Spring Security with ReactJs?

I have secured my API-endpoints of my Spring Boot Application with Spring Security.
On login, I generate a new jwt token and submit it to the user.
On data requests, I expect the user to submit the token in the header.
If I do this using postman, it works perfecty fine.
When I try to send the token with React, it fails (axios/fetch/superagent).
The issue is not the submission of the token itself because if I disable authorization I can read the authorization header with the controller.
Instead, Spring Security somehow does not recognize the header when it is sent via React.
I've tried adding another custom header to see if Spring allows that but that custom header is "blocked" aswell.
React:
axios(apiTest, {
async: true,
crossDomain: true,
method: "GET",
headers: {
Authorization: `Bearer ${this.props.token}`,
TestHeader: "RandomValue",
Accept: "*/*"
},
processData: false,
data: ""
})
.then(res => {
if (res.data !== undefined) {
console.log(res.data);
}
})
.catch(err => console.error(error));
Spring Security:
Token Filter:
#Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
#Value("${jwt.header}")
private String tokenHeader;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
//Usual Authorization header (is null with React-use)
final String requestHeader = request.getHeader(this.tokenHeader);
//Custom header (null)
System.out.println("Test Header: " + request.getHeader("TestHeader"));
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
String authToken = requestHeader.substring(7);
JwtAuthentication authentication = new JwtAuthentication(authToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}
Config:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private JwtAuthenticationProvider jwtAuthenticationProvider;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(jwtAuthenticationProvider);
}
#Bean
CorsFilter corsFilter() {
CorsFilter filter = new CorsFilter();
return filter;
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() {
return new JwtAuthenticationTokenFilter();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.addFilterBefore(corsFilter(), SessionManagementFilter.class).csrf().disable().authorizeRequests()
.antMatchers("/Login").permitAll().antMatchers("/CloseConnection**").permitAll()
.antMatchers(HttpMethod.OPTIONS, "**").permitAll().anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
}
}
Any idea what the exact issue is and how to solve it?
Try this it worked for me
private String getToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return authHeader.replace("Bearer ","");
}
return null;
}
axios call
axios.post(url,{
"data": 'sample',
},
{
headers: {
'Authorization':'Bearer '+token,
'Content-Type':'application/json'
}
})
.then((res) => {
console.log(res);
})

Spring boot does not receive headers from react js

I'm implementing a ReactJs applications. I am using axios to invoke server side services built using Spring Boot. I need to send the header "Authorization: Bearer token-value". This is the client side code:
var options = {
withCredentials: true,
headers: {'Authorization': 'Bearer token-value'}
};
axios.post('http://localhost:9090/services/list', null, options)
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
});
This is the Spring Boot controller:
#RestController
public class ServiceController {
private static final String AUTHORIZATION_HEADER_NAME = "Authorization";
private static final String BEARER = "Bearer ";
private static String getToken(HttpServletRequest request) {
String header = request.getHeader(AUTHORIZATION_HEADER_NAME);
if (header == null || header.trim().equals("")) {
return null;
}
header = header.trim();
if (!header.startsWith(BEARER)) {
return null;
}
return header.substring(BEARER.length()).trim();
}
#GetMapping
#RequestMapping(value = "/services/list", produces = "application/json", method = RequestMethod.POST)
public ResponseEntity<?> getTargets(HttpServletRequest request, HttpServletResponse response) {
String token = getToken(request);
if (token == null) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
DTOObject obj = goForTheBusinessObject(token);
return new ResponseEntity<>(obj, HttpStatus.OK);
}
}
This is the CORS configuration
#Configuration
public class RestConfig {
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("POST");
config.addAllowedMethod("GET");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PUT");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
If I invoke the service using curl I got the expected response:
curl -X POST -H "Authorization: Bearer token-value" http://localhost:9090/services/list
If I invoke the service using post man, again I got the right answer.
But when I executed the ReactJS application, the server never receive the "Authorization" header.
Somebody help me please !!
You are facing CORS issues, Implement this class to resolve this-
#Component
public class CorsFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
if (exchange != null) {
exchange.getResponse().getHeaders().add("Access-Control-Allow-Origin", "*");
exchange.getResponse().getHeaders().add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
exchange.getResponse().getHeaders().add("Access-Control-Allow-Headers",
"DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range");
exchange.getResponse().getHeaders().add("Access-Control-Max-Age", "1728000");
if (exchange.getRequest().getMethod() == HttpMethod.OPTIONS) {
exchange.getResponse().getHeaders().add("Access-Control-Max-Age", "1728000");
exchange.getResponse().setStatusCode(HttpStatus.NO_CONTENT);
return Mono.empty();
} else {
exchange.getResponse().getHeaders().add("Access-Control-Expose-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range");
return chain.filter(exchange);
}
} else {
return chain.filter(exchange);
}
}
}
For more info on CORS visit this
Update: For scanning the component you can do following-
#ComponentScan(value = "com.pck", // cors filter package
useDefaultFilters = false)
public class MainClass {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.
run(MainClass.class, args);
}
}

Resources