0
I’m trying to do a post type resquest with credentials (token) for my spring boot built api.
But I’m having trouble specifying the source I pass as a parameter to the method CorsRegistry.allowOrigins()
.
The two origins I tried were : "http://localhost/*"
and "https://italomp.github.io/interface-CRUD/"
.
The second source is an interface I hosted on github.io
.
But when making the request, I see in the browser console that a preflighted request error occurred.
This error should not happen because I configured the sources, headers and methods that the server accepts and these are the elements that the preflighted request analyzes.
So I ask:
- How to specify the origin
"localhost"
inCorsRegistry.allowOrigins()
? - Why doesn’t it work when I use the origin of
github.io/
?
JAVA CODE - Class with @Springbootapplication annotation:
package com.crud.rest;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
/**
* This is the main class of the project, because it has
* springBootApplication annotation. That's why it
* is the responsible class for uploading the spring server.
*/
@SpringBootApplication
@ComponentScan("com.crud")
public class CrudApplication {
/**
* This static method start the spring server.
* @param args: argument array.
*/
public static void main(String[] args) {
SpringApplication.run(CrudApplication.class, args);
}
/**
* This method is responsible for filtering tokens for
* private routes. And here are noted all the private routes.
* The '*' at the end of the route indicates that all the routes
* beginning with "/v1/product/private/" are being passed as a
* parameter to the addUrlPatterns method.
*/
@Bean
public FilterRegistrationBean filterJwt(){
FilterRegistrationBean filter = new FilterRegistrationBean();
filter.setFilter(new TokenFilter());
filter.addUrlPatterns("/v1/product/private/*");
return filter;
}
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return (WebMvcConfigurer) new WebMvcConfigurerIplmts() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost/*") //this value can't be "*", because allowCredentials(true).
.allowedMethods("PUT", "DELETE","POST","GET")
.allowedHeaders("Content-type", "Authorization")
.allowCredentials(true)
.maxAge(3600);
}
};
}
public class WebMvcConfigurerIplmts implements WebMvcConfigurer{
public WebMvcConfigurerIplmts() {
}
}
}
JAVA CODE - Tokenfilter class:
package com.crud.rest;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.filter.GenericFilterBean;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
/**
* This class is responsible for filtering the received tokens
* in the requests.
* @author Italo Modesto
*/
public class TokenFilter extends GenericFilterBean{
/**
* This method applies a filter on tokens to verify if it's valid.
*
* Firstly, this method converts ServeletRequest to HttpServlerReques
* to be able to access its authorization attribute;
*
* then this method extracts the authorization attribute from
* the request and check if the attribute is null or badly formatted (in
* these cases, exceptions will be throws);
*
* Finally, The token is extracted from the request and unpacked for it
* can be passed as parameter to the filter chain.
*
* The filter chain (in this case) call the resource requested.
*
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String auth = req.getHeader("Authorization");
if(auth == null || !auth.startsWith("Bearer ")) {
throw new ServletException("Token inexistente ou mal formatado");
}
String token = auth.substring(7);
try {
Jwts.parser().setSigningKey("caneca").parseClaimsJws(token).getBody();
}catch(SignatureException e) {
throw new ServletException("Token inválido ou expirado");
}
chain.doFilter(request, response);
}
}
BROWSER CONSOLE ERRORS SHOW:
Access to fetch at 'http://localhost:8080/api/v1/user/' from origin 'null' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
script.js:43
POST http://localhost:8080/api/v1/user/ net::ERR_FAILED
(anonymous) @ script.js:43
index.html:1
Uncaught (in promise) TypeError: Failed to fetch
FRONT JAVASCRIPT CODE
let buttonUserRegistry = document.getElementById("buttonUserRegistry");
let buttonUserLogin = document.getElementById("buttonUserLogin");
let buttonCreateProduct = document.getElementById("buttonCreateProduct");
let buttonReadProduct = document.getElementById("buttonReadProduct");
let buttonUpdateProduct = document.getElementById("buttonUpdateProduct");
let buttonDeleteProduct = document.getElementById("buttonDeleteProduct");
let result = document.getElementById("result");
/**
* Esse trecho de código adiciona um evento ao elemento
* buttonUserRegistry.
*
* evento: click
*
* callback: função que coleta dados do novo usuario no form com
* id "userRegistry" e envia esses dados para p servidor através
* de uma requisição POT.
* Esses daados compõe um novo usuário que será cadastrado no
* servidor
*/
buttonUserRegistry.addEventListener("click", function(){
let form = document.getElementById("userRegistry");
let inputName, inputLogin, inputPassword;
let newUser, registerUrl, optRequest;
for(e of form){
if(e.name == "inputName"){
inputName = e.value;
}
else if(e.name == "inputLogin"){
inputLogin = e.value;
}
else if(e.name == "inputPassword"){
inputPassword = e.value;
}
}
newUser = {"name": inputName,
"login": inputLogin,
"password": inputPassword};
registerUrl = "http://localhost:8080/api/v1/user/";
optRequest = {method: "post",
headers: {"Content-type": "application/json"},
body: JSON.stringify(newUser)};
fetch(registerUrl,optRequest)
.then(resp => result.innerText = resp.status);
});
/**
* Esse trecho de código adiciona um evento ao elemento
* "buttonUserLogin".
*
* evento: click
*
* callback: função que coleta dados do usuário que vai
* efetuar login (esses dados estão no form que usa o
* id "userLogin") e envia uma requisição POST para o
* servidor.
* E ainda, armaena o token recebido do servido no
* localStorage.
*/
buttonUserLogin.addEventListener("click", function(){
let form = document.getElementById("userLogin");
let inputLogin, inputPassword;
let user, loginUrl, optRequest;
for(e of form){
if(e.name == "inputLogin"){
inputLogin = e.value;
}
else if(e.name == "inputPassword"){
inputPassword = e.value;
}
}
user = {"login": inputLogin,
"password": inputPassword};
loginUrl = "http://localhost:8080/api/v1/auth/login";
optRequest = {method: "post",
headers: {"Content-type": "application/json"},
body: JSON.stringify(user)};
fetch(loginUrl, optRequest)
.then(resp => resp.json())
.then(tokenObject => window.localStorage.setItem("token", tokenObject.token))
.then(result.innerText = "Login realizado com sucesso!");
});
buttonCreateProduct.addEventListener("click", function(){
let form = document.getElementById("createProduct");
let inputName, inputDescription, inputPrice;
let newProduct, createProductUrl, optRequest, token;
for(e of form){
if(e.name == "inputName"){
inputName = e.value;
}
else if(e.name == "inputDescription"){
inputDescription = e.value;
}
else if(e.name == "inputPrice"){
inputPrice = e.value;
}
}
newProduct = {"name": inputName,
"description": inputDescription,
"price": inputPrice};
createProductUrl = "http://localhost:8080/api/v1/product/private/";
token = window.localStorage.getItem("token");
optRequest = {method: "post",
headers: {"Content-type":"application/json",
"Authorization":"Bearer " + token},
credentials: "include",
body: JSON.stringify(newProduct)};
fetch(createProductUrl, optRequest)
.then(resp => {result.innerText = resp.status;});
console.log(token);
});
/*
let inputName;
let inputLogin;
let inputPassword;
let inputDescription;
let inputPrice;
let inputProductID;
function inputCollector(){
}
for(e of form){
if(e.name == "inputName"){
inputName = e.value;
}
else if(e.name == "inputLogin"){
inputLogin = e.value;
}
else if(e.name == "inputPassword"){
inputPassword = e.value;
}
else if(e.name == "inputDescription"){
inputDescription = e.value;
}
else if(e.name == "inputPrice"){
inputPrice = e.value;
}
else if(e.name == "inputProductID"){
inputProductID = e.value;
}
}
*/`
kicking here, but I believe you need to specify the door, if you let. allowedOrigins("http://localhost/*") I believe it will consider port 80
– Lucas Miranda
For the origin "https://italomp.github.io/interface-CRUD/" I made an attempt specifying the port (https://italomp.github.io/interface-CRUD/:80), but it did not work. For localhost origin I don’t know which port I should specify as I think my local application will run on different ports each time I run the application...
– Italo Modesto Pereira
the right would be italomp.github.io:80/interface-CRUD, but as I said earlier, when you do not put anything it already assumes that the port is 80, in your local case what I imagine localhost:8080, since it is what your front is calling, Another thing I noticed is that you’re trying to send an authorization token but I didn’t see on your back where it’s being treated, anyway, it’s hazy, I think you need to give more details
– Lucas Miranda
I edited my question and added the class responsible for checking the validity of the token, Lucas. But I can make this request using the Postman program, so I believe that there is no problem in the token processing, but in the CORS itself. This is the link to the repository where the code (API) is hosted: https://github.com/italomp/API-RESTful-using-spring-boot
– Italo Modesto Pereira
opened your project here and tested using the github.io api,, in allowedOrigins I left only "https://italomp.github.io" and it worked normally, I believe that locally if you put "localhost:8080" will also work
– Lucas Miranda