问题
I have a simple rest API which works with database. It worked properly until I added the security part. Now it gives HTTP 405 Not Allowed on the POST and DELETE requests. I have no idea why. The GET requests work properly.
So here is the controller class:
@Controller
public class MarkerController {
private Logger logger = Logger.getLogger(MarkerController.class.getName());
@Autowired
private MarkerServiceInterface markerService;
@RequestMapping(value="/markers", method=RequestMethod.GET)
public @ResponseBody List<Marker> getMarkers(@RequestParam(value="city", defaultValue="") String city) {
logger.info("HANDLE GET REQUEST");
return this.markerService.getAllMarkers();
}
@RequestMapping(value="/markers/new", method=RequestMethod.POST)
public @ResponseBody Marker addMarker(@RequestBody Marker marker) {
logger.info("HANDLE POST REQUEST");
this.markerService.addMarker(marker);
return marker;
}
@RequestMapping(value="/markers/delete", method=RequestMethod.DELETE)
public @ResponseBody String deleteMarker(@RequestParam(value="id", defaultValue="") String id) {
logger.info("HANDLE DELETE REQUEST");
if (!id.equals("")) {
logger.info(id);
this.markerService.deleteMarker(Long.parseLong(id));
}
return "";
}
@RequestMapping(value="/admin/map")
public String trafficSpy() {
logger.info("HANDLE MAP");
return "index";
}
@RequestMapping(value="/admin")
public String admin() {
return "admin";
}
@RequestMapping(value="/login")
public String login() {
return "login";
}
}
This is the SecurityConfig:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
UserDetailsService userDetailsService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(
passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**")
.access("hasRole('ROLE_ADMIN')")
.antMatchers("/markers/**")
.access("hasRole('ROLE_USER')")
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.and()
.csrf()
.and()
.exceptionHandling()
.accessDeniedPage("/403");
}
@Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
@Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}
The DELETE request is called with the following ajax code:
$.ajax({
url: "localhost:8080/markers/delete?id=" + currentMarker.get("id"),
type: 'DELETE',
success: function(result) {
console.log(result);
}
});
And here is the message given in the console:
2015-05-11 15:48:13.671 WARN 8279 --- [nio-8181-exec-6] o.s.web.servlet.PageNotFound : Request method 'DELETE' not supported
These are the headers of the response. I can see that in AlLLOW I have only GET and HEAD. So if I'm right, this means that the method in the controller accepts only GET and HEAD requests.
(Status-Line) HTTP/1.1 405 Method Not Allowed
Server Apache-Coyote/1.1
x-content-type-options nosniff
x-xss-protection 1; mode=block
Cache-Control no-cache, no-store, max-age=0, must-revalidate
Pragma no-cache
Expires 0
X-Frame-Options DENY
Allow GET, HEAD
Content-Type application/json;charset=UTF-8
Transfer-Encoding chunked
Date Mon, 11 May 2015 17:35:31 GMT
In the response I have this exeption:
org.springframework.web.HttpRequestMethodNotSupportedException
Any idea what is causing this problem? How can I allow the POST and DELETE methods?
回答1:
You forget the csrf-Token.
It's recommended that you add the csrf-Token in the meta-tag. You can read it in the Spring Security Documentation
With this you can do the following:
$(function () {
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);
});
});
回答2:
I wanted to leave an additional solution, because although Manu Zi's answer is a correct one, it wasn't immediately clear to me why the first time I came across this issue and found this answer.
The underlying issue was obscured by the immediate problem of the 405 Method Not Allowed.
In my case, there were two factors at play. Firstly, there was no POST method for my AccessDenied controller, which resulted in a 405 when a POST method was denied and redirected to the AccessDenied controller. This was only evident after turning up debug logging on org.springframework.web.
Once that was clear, it was a matter of figuring out why I was getting access denied. All the permissions and roles were correct, but having upgraded from Spring 3 to 4, I found that CSRF protection was enabled by default. It either needs to be disabled, or you have to use Manu Zi's solution.
To disable it, see: spring security 4 csrf disable via xml
来源:https://stackoverflow.com/questions/30171265/http-405-not-allowed-spring-boot-spring-security