Reputation: 402
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:
EDIT: the [GET]
requests are working correctly, but [PUT,POST,DELETE]
do not work.
Upvotes: 7
Views: 9517
Reputation: 402
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
Upvotes: 5
Reputation: 13727
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.
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);
});
Upvotes: 10