5

I have a website based on spring boot, spring-security, thymeleaf, I also use ajax in some cases.

Issue: I am using form login security in spring security. In the browser, after I login I can consume rest API (GET), but using Ajax, it returns a http 403 error, even if my Ajax request contains session id in cookies.

Security config:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
    .antMatchers("/admin/**").hasRole("ADMIN")
    .antMatchers("/rest/**").hasRole("ADMIN")
            .anyRequest().permitAll()
     .and()
     .formLogin().loginPage("/sign-in-up")
            .loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/");

}

REST API I test it correctly:

@RestController
@RequestMapping("rest/categories")
public class CategoriesRest {
@Autowired
private CategoryService categoryService;

@GetMapping("/")
public ResponseEntity<List<Category>> findAll() {
    List<Category> all = categoryService.getAll();
    if (all.isEmpty()) {
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<>(all, HttpStatus.OK);
}

@GetMapping("/{id}")
public ResponseEntity<Category> findById(@PathVariable int id) {
    Category obj = categoryService.get(id);
    if (obj == null) {
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<>(obj, HttpStatus.OK);
}

@PostMapping("/")
public ResponseEntity<Category> createMainSlider(@RequestBody Category obj) {
    System.out.println("-------rest Post");

    return new ResponseEntity<>(categoryService.add(obj), HttpStatus.CREATED);
}

@PutMapping("/{id}")
public ResponseEntity<Category> update(@RequestBody Category obj, @PathVariable int id) {
    Category obj1 = categoryService.update(obj);

    System.out.println(obj);
    return new ResponseEntity<>(obj1, HttpStatus.OK);
}

@DeleteMapping("/{id}")
public ResponseEntity<Category> deleteEmp(@PathVariable int id) {
    categoryService.delete(id);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

}

My Ajax code:

$('.deleteBtn').bind('click',function(e){
        e.preventDefault();
        $.ajax({
            type:'DELETE',
            url : "/rest/categories/"+$(e.currentTarget).data('id'),
             xhrFields: {
                  withCredentials: true
               },
             success : function(result) {
                 location.reload();
                 console.log(result);
               },
              error : function(e) {
                alert("Error!")
                console.log("ERROR: ", e);
              }
        })
    })

My ajax request header like this:

ajax request header

EDIT: the [GET] requests are working correctly, but [PUT,POST,DELETE] do not work.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
MuhammedH
  • 352
  • 4
  • 17
  • Hello, Hamod are you facing 403 for only for ` url : "/rest/categories/"+$(e.currentTarget).data('id'),` does other APIs are working or giving 403? – Romil Patel Sep 18 '19 at 14:24
  • what I mean is are you able to get the response for other than DELETE method or facing 403 for all methods and APIs? – Romil Patel Sep 18 '19 at 14:30
  • If you look at your request from the client to the server, the `Authorization` header is being set and it is sending Basic authentication with a Base64 encoded string. That is most likely occurring from the `withCredentials: true` in your javascript. I would try removing that and see what happens. – hooknc Sep 18 '19 at 15:35
  • yes that is true i put 'withCredentials: true' try to solve the problem but actually it is the same problem, i removed it and not solved – MuhammedH Sep 18 '19 at 15:45
  • @PatelRomil i found that [GET] requests are working, but not the others!! – MuhammedH Sep 18 '19 at 15:57

2 Answers2

9

Why .csrf().disable().cors() has worked?

CSRF stands for Cross Site Request Forgery

In simple words, it is one kind of token which is sent with the request to prevent the attacks. In order to use the Spring Security CSRF protection, we'll first need to make sure we use the proper HTTP methods for anything that modifies the state (PATCH, POST, PUT, and DELETE – not GET).

Some frameworks handle invalid CSRF tokens by invaliding the user’s session, but this causes its own problems. Instead by default Spring Security’s CSRF protection will produce an HTTP 403 access denied.

Ajax and JSON Requests

If you are using JSON, then it is not possible to submit the CSRF token within an HTTP parameter. Instead, you can submit the token within an HTTP header. A typical pattern would be to include the CSRF token within your meta tags

<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>

//jQuery
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");

$(document).ajaxSend(function(e, xhr, options) {
    xhr.setRequestHeader(header, token);
});
Romil Patel
  • 12,879
  • 7
  • 47
  • 76
4

thanks everybody
I solved it by disabling the CSRF, i added this

.csrf().disable().cors()

so in my spring security configuration:

        http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/rest/**").hasRole("ADMIN")
            .anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up")
            .loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
            .and()
            .csrf().disable().cors();

====== EDIT:
@Patel Answered with a useful explanation ... thanks to him

MuhammedH
  • 352
  • 4
  • 17