Reputation: 140
I have a spring boot project that uses spring security with JWT token. This works fine in POSTMAN but it gives a 403 error when using react axios. Below is the code used
SecurityConfig.java
package com.cg.practice.EmployeeCRUD.config;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import com.cg.practice.EmployeeCRUD.Service.CustomUserService;
import com.cg.practice.EmployeeCRUD.filter.EmployeeCRUDFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
CustomUserService userService;
@Autowired
EmployeeCRUDFilter employeeCRUDFilter;
/*
* @Autowired CorsFilter corsFilter;
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
System.out.println("Hi1");
auth.userDetailsService(userService);
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) {
// TODO Auto-generated method stub
System.out.println("Hi2");
try {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
corsConfiguration.setAllowedOrigins(List.of("http://localhost:3000"));
corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PUT","OPTIONS","PATCH", "DELETE"));
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setExposedHeaders(List.of("Authorization"));
http
// .addFilterBefore(corsFilter, SessionManagementFilter.class)
.csrf().disable().authorizeRequests().antMatchers("/authenticate/")
.permitAll().anyRequest().authenticated()
.and().exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().cors().configurationSource(request -> corsConfiguration);
;
http.addFilterBefore(employeeCRUDFilter, UsernamePasswordAuthenticationFilter.class);
}
catch (Exception e)
{
// TODO: handle exception
e.printStackTrace();
}
}
}
EmployeeCRUDFilter.java
package com.cg.practice.EmployeeCRUD.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.cg.practice.EmployeeCRUD.Service.CustomUserService;
import com.cg.practice.EmployeeCRUD.util.JwtUtil;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@Component
public class EmployeeCRUDFilter extends OncePerRequestFilter {
@Autowired
JwtUtil jwtUtil;
@Autowired
CustomUserService userService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
{
// TODO Auto-generated method stub
String authorizationHeader = request.getHeader("Authorization");
String token = null;
String userName = null;
try
{
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
token = authorizationHeader.substring(7);
userName = jwtUtil.extractUsername(token);
}
if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userService.loadUserByUsername(userName);
if (jwtUtil.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
CRUDController method
@PostMapping("/authenticate")
public String generateToken(@RequestBody AuthRequest authRequest) throws Exception
{
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authRequest.getUserName(),authRequest.getPassword())
);
}
catch (Exception e) {
return "Invalid User/Password";
}
return jwtUtil.generateToken(authRequest.getUserName());
}
React Authservice.js
import http from '../Mycomponent/http-common';
class AuthService{
authenticate(){
return http.post("/authenticate", {"userName":"Dip","password":"password@123"}
)
.then(response => {
console.log(response.data)
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
.catch(error =>{
console.log("Error :"+error);
});
}
}
export default new AuthService();
http-common.js
import axios from 'axios';
export default axios.create({
baseURL: "http://localhost:8080/CRUD",
headers: {
"Content-type": "application/json"
}
});
This gives a 403 error in browser
Failed to load resource: the server responded with a status of 403 ()
AuthService.js:16 Error :Error: Request failed with status code 403
:8080/CRUD/getAll:1 Failed to load resource: the server responded with a status of 403 ()
createError.js:16 Uncaught (in promise) Error: Request failed with status code 403
at createError (createError.js:16:1)
at settle (settle.js:17:1)
at XMLHttpRequest.onloadend (xhr.js:66:1)
Can anyone please help. I am stuck on it for a long time. Am I missing something? I am new to react.
Works fine in PostMAN
Upvotes: 1
Views: 5860
Reputation: 12342
I think here lies the problem:
http
.csrf().disable().authorizeRequests().antMatchers("/authenticate/")
.permitAll().anyRequest().authenticated()
.and().exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().cors().configurationSource(request -> corsConfiguration);
You tell spring to permitAll
requests to the /authenticate/
endpoint, and require authentication for all the other requests. But from the frontend you're making a request to /CRUD/authenticate/
. That's why you get 403, because this path must be authenticated - meaning that the request must already have the Authorization
header. I think it should work if you change the first line to this:
.csrf().disable().authorizeRequests().antMatchers("/CRUD/authenticate/")
Upvotes: 0
Reputation: 11
Seems like you have missed Authorization
in the header when you setup your axios instance
Upvotes: 1