title: Getting a Post 403 Forbidden with Spring Boot (VueJS and Axios Frontend)
body: <p>I've been having an issue with CORS and I have tried <em>everything</em> I could find on Stack Overflow and basically anything that I found on Google and have had no luck. </p>
  
  <p>So I have user authentication on my backend and I have a login page on my frontend. I hooked up the login page with Axios so I could make a post request and tried to login but I kept getting errors like "Preflight request" so I fixed that then I started getting the "Post 403 Forbidden" error. </p>
  
  <p>It appeared like this:</p>
  
  <pre><code>POST http://localhost:8080/api/v1/login/ 403 (Forbidden)
  </code></pre>
  
  <p>Even trying to login using Postman doesn't work so something is clearly wrong. Will be posting class files below</p>
  
  <p>On my backend, I have a class called WebSecurityConfig which deals with all the CORS stuff:</p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Autowired
      private UserDetailsServiceImpl userDetailsService;
  
      @Bean
      public WebMvcConfigurer corsConfigurer() {
          return new WebMvcConfigurerAdapter() {
              @Override
              public void addCorsMappings(CorsRegistry registry) {
                  registry.addMapping("/**")
                          .allowedMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS");
              }
          };
      }
  
      @Bean
      public CorsFilter corsFilter() {
          UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
          CorsConfiguration config = new CorsConfiguration();
          config.setAllowCredentials(true);
          config.addAllowedOrigin("*");  // TODO: lock down before deploying
          config.addAllowedHeader("*");
          config.addExposedHeader(HttpHeaders.AUTHORIZATION);
          config.addAllowedMethod("*");
          source.registerCorsConfiguration("/**", config);
          return new CorsFilter(source);
      }
  
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.headers().frameOptions().disable();
          http
                  .cors()
                  .and()
                  .csrf().disable().authorizeRequests()
                  .antMatchers("/").permitAll()
                  .antMatchers("/h2/**").permitAll()
                  .antMatchers(HttpMethod.POST, "/api/v1/login").permitAll()
                  .anyRequest().authenticated()
                  .and()
                  // We filter the api/login requests
                  .addFilterBefore(new JWTLoginFilter("/api/v1/login", authenticationManager()),
                          UsernamePasswordAuthenticationFilter.class);
          // And filter other requests to check the presence of JWT in header
          //.addFilterBefore(new JWTAuthenticationFilter(),
          //       UsernamePasswordAuthenticationFilter.class);
      }
  
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          // Create a default account
          auth.userDetailsService(userDetailsService);
  //        auth.inMemoryAuthentication()
  //                .withUser("admin")
  //                .password("password")
  //                .roles("ADMIN");
      }
  }
  </code></pre>
  
  <p>On our frontend which is written in VueJS and using Axios to make the call</p>
  
  <pre><code>&lt;script&gt;
      import { mapActions } from 'vuex';
      import { required, username, minLength } from 'vuelidate/lib/validators';
  
      export default {
          data() {
              return {
                  form: {
                      username: '',
                      password: ''
                  },
                  e1: true,
                  response: ''
              }
          },
          validations: {
              form: {
                  username: {
                      required
                  },
                  password: {
                      required
                  }
              }
          },
          methods: {
              ...mapActions({
                  setToken: 'setToken',
                  setUser: 'setUser'
              }),
              login() {
                  this.response = '';
                  let req = {
                      "username": this.form.username,
                      "password": this.form.password
                  };
  
                  this.$http.post('/api/v1/login/', req)
                  .then(response =&gt; {
                      if (response.status === 200) {
                          this.setToken(response.data.token);
                          this.setUser(response.data.user);
  
                          this.$router.push('/dashboard');
                      } else {
                          this.response = response.data.error.message;
                      }
                  }, error =&gt; {
                      console.log(error);
                      this.response = 'Unable to connect to server.';
                  });
              }
          }
      }
  &lt;/script&gt;
  </code></pre>
  
  <p>So when I debugged via Chrome's tools (Network), I noticed that the OPTIONS request goes through as shown below:</p>
  
  <p><a href="https://i.stack.imgur.com/0Z8mo.png" rel="noreferrer"><img src="https://i.stack.imgur.com/0Z8mo.png" alt="OPTIONS request going through"></a></p>
  
  <p>Here is a picture of the POST error:</p>
  
  <p><a href="https://i.stack.imgur.com/9pr8X.png" rel="noreferrer"><img src="https://i.stack.imgur.com/9pr8X.png" alt="POST Request Error"></a></p>
  
  <p>Here is another class which handles the OPTIONS request (JWTLoginFilter as referenced in the WebSecurityConfig):</p>
  
  <pre><code>public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
  
      public JWTLoginFilter(String url, AuthenticationManager authManager) {
          super(new AntPathRequestMatcher(url));
          setAuthenticationManager(authManager);
  
      }
  
      @Override
      public Authentication attemptAuthentication(
              HttpServletRequest req, HttpServletResponse res)
              throws AuthenticationException, IOException, ServletException {
          AccountCredentials creds = new ObjectMapper()
                  .readValue(req.getInputStream(), AccountCredentials.class);
          if (CorsUtils.isPreFlightRequest(req)) {
              res.setStatus(HttpServletResponse.SC_OK);
              return null;
  
          }
          return getAuthenticationManager().authenticate(
                  new UsernamePasswordAuthenticationToken(
                          creds.getUsername(),
                          creds.getPassword(),
                          Collections.emptyList()
  
                  )
          );
      }
  
      @Override
      protected void successfulAuthentication(
              HttpServletRequest req,
              HttpServletResponse res, FilterChain chain,
              Authentication auth) throws IOException, ServletException {
          TokenAuthenticationService
                  .addAuthentication(res, auth.getName());
      }
  }
  </code></pre>