-1

I am learning web development and I have frontend vue.js app, which is making request to the spring boot back end. When I make a requst from vue to the spring boot with JWT authorization token, I get CORS error. I tried to put a @CrossOrigin(origin="*") anotation to my controller classes, but it didn´t help. I made a CorsConfig class, but that didn´t help neither. I still get CORS error:

"Access to fetch at 'http://127.0.0.1:8081/api/v1/posts/userPosts' from origin 'http://localhost:8080' 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."

"PostService.js:12 GET http://127.0.0.1:8081/api/v1/posts/userPosts net::ERR_FAILED"

My CorsConfig class:

package com.blog.security.config;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/v1/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

My security config class:

package com.blog.security.config;

import com.blog.user.User;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(@NotNull HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf()
                .disable()
                .authorizeHttpRequests()
                .requestMatchers("/api/v1/posts","/api/v1/auth/register","/api/v1/auth/login")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
        
        return httpSecurity.build();
    }



}


My controller:

package com.blog.post;

import com.blog.post.exceptions.PostNotModifiableException;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api/v1/posts")
public class PostController {
    private final PostService postService;


    public PostController(PostService postService){
        this.postService = postService;
    }

    @ExceptionHandler(PostNotModifiableException.class)
    public ResponseEntity<PostResponse> handleException(PostNotModifiableException e){
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(PostResponse.builder().postResponse(e.getMessage()).build());
    }
    @ExceptionHandler(ExpiredJwtException.class)
    public ResponseEntity<PostResponse> handleException(ExpiredJwtException e) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(PostResponse.builder().postResponse(e.getMessage()).build());
    }
    @ExceptionHandler(JwtException.class)
    public ResponseEntity<PostResponse> handleException(JwtException e) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(PostResponse.builder().postResponse(e.getMessage()).build());
    }
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<PostResponse> handleException(RuntimeException e){
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(PostResponse.builder().postResponse(e.getMessage()).build());
    }

    @GetMapping
    public List<Post> getPosts(){return postService.getAllPosts();}

    @GetMapping("userPosts")
    public ResponseEntity<?> getUserPosts(HttpServletRequest request) {
        try {
            List<Post> posts = postService.getUserPosts(request);
            return ResponseEntity.ok(posts);
        } catch (ExpiredJwtException e) {
            String errorMessage = "JWT token has expired";
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(errorMessage);
        }
    }

    @PostMapping("createPost")
    public ResponseEntity<PostResponse> addPost(@RequestBody PostRequest postRequest, HttpServletRequest request){
        System.out.println("Saving post");
        postService.addPost(postRequest, request);
        return ResponseEntity.ok().body(PostResponse.builder().postResponse("Post created").build());
    }


    @PutMapping("update/{postId}")
    public ResponseEntity<PostResponse> updatePost(@PathVariable("postId") Integer postId, @RequestBody PostRequest updateRequest, HttpServletRequest request) throws Exception {
        postService.updatePost(postId, updateRequest, request);
        return ResponseEntity.ok().body(PostResponse.builder().postResponse("Post updated").build());
    }



    @DeleteMapping("delete/{postId}")
    public ResponseEntity<PostResponse> deletePost(@PathVariable("postId") Integer postId, HttpServletRequest request) throws Exception{
        System.out.println("Deleting post");
        postService.deletePost(postId, request);
        return ResponseEntity.ok().body(PostResponse.builder().postResponse("Post deleted").build());
    }

}

My vue service for posts:


export default{
    async fetchData() {
            let response = await fetch("http://127.0.0.1:8081/api/v1/posts")
            let data = await response.json()
            return data;
        },

        async fetchUserData() {
            
            let response = await fetch("http://127.0.0.1:8081/api/v1/posts/userPosts",{
                method:"GET",
                headers:{
                    "Authorization": "Bearer " + sessionStorage.getItem("JWT"),
                    "Content-Type": "application/json",

                }
            })
            let data = await response.json()
            return data;
        },
        async savePost(head, body) {
            console.log(sessionStorage.getItem("JWT"))
            await fetch("http://127.0.0.1:8081/api/v1/posts/createPost",{
                method:"POST",
                headers:{
                    "Authorization": "Bearer " + sessionStorage.getItem("JWT"),
                    "Content-Type": "application/json",
        

                },
                body:JSON.stringify({
                    "head":head,
                    "body":body
                })
            })
            return "Post created";
        }
};



I tried to configure the CORS globally or locally for controller class or even for every request mapping, but nothing works.

If I changed the requestmatcher in my security config to:

.requestMatchers("/api/v1/**","/api/v1/auth/register","/api/v1/auth/login") 

to acces everything without athentication, it works. But if I leave it to everything esle than registering and login to authenticated only, it does not.

  • is this a spring MVC application? also dont use JWTs as sessions as it is insecure, spring security does not have jwt filter because using jwts the way you are doing it is insecure. Whatever blogpost you have followed has no idea how seccurity works. JWTs were never ment to be used as session holders. So please delete that filter and implement formlogin using the spring security documentation instead – Toerktumlare Jul 30 '23 at 20:20
  • Probably related to [this discussion](https://stackoverflow.com/questions/76648712/spring-boot-post-request-error-blocking-cors-policy/76651003#76651003) – Eric Jul 31 '23 at 05:08
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community Jul 31 '23 at 09:56

1 Answers1

-1

We can add the following configuration to ensure cors requests are handled first:

    @Bean
    public SecurityFilterChain securityFilterChain(@NotNull HttpSecurity httpSecurity) throws Exception {
        httpSecurity

                .cors().and()
                .csrf()
                ...

Please see this answer for more details.

SwathiP
  • 315
  • 3
  • 5