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);
}
};
}
Related
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
I am trying to update the data using spring boot and angular, but whenever i try to update the data i got this error 'http://localhost:4200' 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.
This is my spring controller and angular service.
I tried other solutions from stackoverflow but it doesn't work
please tell me what i am doing wrong here..
InfoController.java
#RestController
#RequestMapping("/student")
public class InfoController {
#Autowired
private InfoDAO infoDAO;
#CrossOrigin(origins = "http://localhost:4200")
#PutMapping("/infos/{id}")
public List<Info> updateStudent(#RequestBody Info info, #PathVariable int id) {
List<Info> information = infoDAO.getbyID(id);
System.out.println("this is id");
info.setId(id);
infoDAO.update(info);
// info1.update(info1);
return information;
}
}
InfoDAO.java
List<Info> getbyID(int id);
boolean update(Info info);
InfoDAOImpl.java
public class InfoDAOImpl implements InfoDAO {
#PersistenceContext
#Autowired
private EntityManager em;
#Override
public List<Info> getbyID(int id) {
String query = "FROM Info WHERE id = :id";
return em
.createQuery(query,Info.class)
.setParameter("id",id)
.getResultList();
}
public boolean update(Info info) {
try {
em
.merge(info);
return true;
}
catch(Exception ex) {
return false;
}
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/login").hasRole("ADMIN")
.antMatchers("/Signup").hasRole("USER")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Origin","Authorization", "Cache-Control", "Content-Type", "xsrfheadername","xsrfcookiename"
,"X-Requested-With","XSRF-TOKEN","Accept", "x-xsrf-token","withcredentials","x-csrftoken"));
configuration.setExposedHeaders(Arrays.asList("custom-header1", "custom-header2"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Web.service.ts
export class WebService {
constructor(private httpClient: HttpClient) { }
serverUrl = 'http://localhost:8083/student';
editPlan(data: Student, id:any): Observable<any> {
const url = `/infos/${id}`;
return this.httpClient.put(this.serverUrl + url, data);
}
getWebPlanInfo(): Observable<any> {
const url = '/plan/info';
return this.httpClient.get(this.serverUrl + url);
}
}
This issue is with this Line cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues()).
If you remove this line it will work.
But how ? That is because your #Bean method corsConfigurationSource will be loaded by spring container during the runtime and it will create the Bean for this.
By adding this line cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues()) it overrides the Bean corsConfigurationSource.
This method applyPermitDefaultValues() in CorsConfiguration class will allow GET, POST, HEAD request methods by default. Due to this your PUT/DELETE method is not working.
Reference: https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java#L428
In your spring security configuration use the following, so that the corsconfiguration bean that you are creating is automatically taken up by spring instead of the configuration that you provided in the http bean itself.In your configuration you were using the new operator to create an instance yourself manually rather than leaving it to spring to autowire the corsconfiguration bean that you provided below . So try like :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/login").hasRole("ADMIN")
.antMatchers("/Signup").hasRole("USER")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
configuration.setExposedHeaders(Arrays.asList("custom-header1", "custom-header2"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
In theory, if you have setup everything right spring security should automatically add the response headers like Access-Control-Allow-Origin to your response. Official spring secutiy doc official cors doc
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.
I have been trying to solve this cors error for hours and I tried every possible solution except one (which is adding options method for every resource/request).. You can find every tried things below;
Cors-Configuration Class
#Configuration
public class CorsConfiguration
{
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.allowedOrigins("*")
.exposedHeaders(AuthorizationController.AUTHENTICATION_KEY_NAME + "," +
HandlerHelper.REASON_KEYNAME)
.maxAge(3600);
}
};
}
}
Pre Handle
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Methods" ,"GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers",AuthorizationController.AUTHENTICATION_KEY_NAME +","+ REASON_KEYNAME);
response.setHeader("Access-Control-Max-Age","3600"); }
application.properties
spring.mvc.dispatch-options-request=true
Adding both annotation to class and OPTIONS method to any request per resource
#CrossOrigin(origins = "*", maxAge = 3600)
#RequestMapping(value = "/**", method = RequestMethod.OPTIONS)
public ResponseEntity handle() {
return new ResponseEntity(HttpStatus.OK);
}
How can i allow 'not simple cors request' in spring boot? Or is this react issue? My front-end developer cant send request from axios..
Adding code below fixed the problem.
#EnableWebSecurity
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// ...
http.cors().and().csrf().disable();
}
}
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);
})