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);
}
}
Related
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 :)
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 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);
})
I have done an extensive search for the mentioned issue, but unable to find a workable solution.
Kindly have a look on some imp codes and suggest.
// My factory method that returns a promise
contactBackend : function(requestedMethod, requestedUrl,
requestedData) {
return $http({
method : requestedMethod,
url : backend + requestedUrl,
data : requestedData
});
}
//Actual Login method that calls the
loginC.validateLogin = function() {
welcomeMFactory.contactBackend("POST", "/rs/login",
loginC.user).then(
function(success) {
var msg = success.data.loginMsg;
if (msg == "login.valid") {
alert(JSON.stringify(success));
welcomeMFactory.moveToWidget("/home");
} else {
loginC.error = welcomeMFactory.printMsg(
true, msg);
}
},
function(error) {
loginC.error = welcomeMFactory.printMsg(true,
"Unable to reach backend for login");
});
}
// SpringController.xml
<mvc:cors>
<mvc:mapping path="/**" allowed-origins="http://localhost:8080"
allowed-headers="content-type,authentication" exposed-headers="content-type,authentication" />
</mvc:cors>
//Login Controller
#Autowired
private LoginRespI response;
#Autowired
private ProxyHandler proxyHandler;
#Autowired
private LoginServiceImpl loginServiceImpl;
#RequestMapping(method = RequestMethod.POST)
public LoginRespB authenticateUserLogin(#RequestBody LoginReqB request, HttpServletResponse resp) {
LoginDTO loginDTO = loginServiceImpl.validateLoginService(request.getUsername(), request.getPassword());
if (loginDTO != null) {
response.setLoginMsg("login.valid");
} else {
response.setLoginMsg("login.invalid");
}
Claims claims = Jwts.claims().setSubject("ABCDE");
claims.put("ID", "12345");
String toke = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, "ABCDE").compact();
resp.setHeader("Authentication", "Bearer: " + toke);
return (LoginRespB) (proxyHandler.getTargetObject(response));
}
OPTIONS req/resp headers
Login req/resp headers
Add this method to your code ,it will allow cros domain request
package org.codingpedia.demo.rest.util;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MultivaluedMap;
public class CORSResponseFilter
implements ContainerResponseFilter {
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
headers.add("Access-Control-Allow-Origin", "*");
//headers.add("Access-Control-Allow-Origin", "http://podcastpedia.org"); //allows CORS requests only coming from podcastpedia.org
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
headers.add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Codingpedia");
}
}
Have a problem with CORS filter, i think.
Because when i send request with Authorization header by Intellij Idea REST Tools, my filter catch a Authorization header.
But when i try to send request from client side from another server, filter does not see my header(return null).
I`m using spring boot, angularjs, salelizer and JWT for build token.
Params for building token on server side.
private static final JWSHeader JWT_HEADER = new JWSHeader(JWSAlgorithm.HS256);
private static final String TOKEN_SECRET = "Bearer";
public static final String AUTH_HEADER_KEY = "Authorization";
My Auth filter
public class AuthFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String authHeader = httpRequest.getHeader(AuthUtils.AUTH_HEADER_KEY);
if (StringUtils.isBlank(authHeader) || authHeader.split(" ").length != 2) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, AUTH_ERROR_MSG);
} else {
JWTClaimsSet claimSet = null;
try {
claimSet = (JWTClaimsSet) AuthUtils.decodeToken(authHeader);
} catch (ParseException e) {
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, JWT_ERROR_MSG);
return;
} catch (JOSEException e) {
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, JWT_INVALID_MSG);
return;
}
// ensure that the token is not expired
if (new DateTime(claimSet.getExpirationTime()).isBefore(DateTime.now())) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, EXPIRE_ERROR_MSG);
} else {
chain.doFilter(request, response);
}
}
}
#Override
public void destroy() { /* unused */ }
#Override
public void init(FilterConfig filterConfig) throws ServletException { /* unused */ }
}
My CORS filter in Web Mvc config file
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addExposedHeader("Authorization");
config.addExposedHeader("Content-Type");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
My security configure
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"**").permitAll().and().authorizeRequests()
.antMatchers( "/index","/api/**", "/auth/**", "/js/**", "/css/**", "/html/**")
.permitAll().anyRequest().authenticated();
My cliend side configs
function configHttp($httpProvider, $authProvider){
console.log("sdfd");
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
$httpProvider.defaults.headers.common["Accept"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
var token = sessionStorage.getItem("satellizer_token");
if (token && $authProvider.httpInterceptor) {
token = $authProvider.authHeader === 'Authorization' ? 'Bearer ' + token : token;
$httpProvider.defaults.headers.common[$authProvider.authHeader] = token;
}
}
function configAuth($authProvider) {
$authProvider.httpInterceptor = function() { return true; };
$authProvider.baseUrl = 'http://localhost:8080';
$authProvider.loginUrl = '/auth/login';
$authProvider.signupUrl = '/auth/registration';
$authProvider.tokenName = 'token';
$authProvider.storageType = 'sessionStorage';
$authProvider.authToken = 'Bearer';
$authProvider.authHeader = 'Authorization';
}
There are a few options described here.
One option would be to annotate your controller method or class with #CrossOrigin.
If you want global config, you could add a new bean. I took this from the Spring doc listed above and modified it so that the mapping is /*. You can modify that path to be suitable for your application. According to the javadoc all origins will be allowed by default.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/*");
}
};
}