Spring安全性导致通过Ajax的PUT和POST请求出错

时间:2016-09-14 18:20:36

标签: ajax spring-security spring-boot

我有一个小的ajax请求导致exe PUT http://localhost:8080/carwash/add 405 (Method Not Allowed)。你能解释一下我的问题在哪里吗?

Ajax请求

$.ajax({
        url: '/carwash/add',
        dataType: 'json',
        type: 'PUT',
        success: function(data) {
            this.setState({});
        }.bind(this),
        error: function(xhr, status, err) {
            console.error('/carwash/add', status, err.toString());
        }.bind(this)
    })

我的应用程序是由spring security安装的,它具有以下配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/", "/login", "/logout", "/resources/*").permitAll()
            .antMatchers("/owner").access("hasRole('ROLE_OWNER')")
            .antMatchers("/admin").access("hasRole('ROLE_ADMIN')")
            .antMatchers("/carwash").access("hasRole('ROLE_OWNER')")
            .and().formLogin().loginPage("/login").successHandler(authenticationSuccessHandler)
                .usernameParameter("username").passwordParameter("password")
            .and().exceptionHandling().accessDeniedPage("/login?error");
}

注意:调用ajax requst的用户有角色'ROLE_OWNER',可以毫无问题地到达“/ owner”和“/ carwash”。

我的控制器也可以放置请求:

@RequestMapping(value = "/carwash/add", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public void addCarWashPOST(){
    System.out.println("inside addCarWash");
}

@RequestMapping(value = "/carwash/add", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public void addCarWashPUT(){
    System.out.println("inside addCarWash");
}

我的所有应用程序都基于弹簧启动,因此我也在此PUT请求后记录:

DEBUG 12640 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet        : Successfully completed request
DEBUG 12640 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing PUT request for [/login]
DEBUG 12640 --- [io-8080-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /login
DEBUG 12640 --- [io-8080-exec-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported
DEBUG 12640 --- [io-8080-exec-10] .w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported
DEBUG 12640 --- [io-8080-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported
WARN 12640 --- [io-8080-exec-10] o.s.web.servlet.PageNotFound             : Request method 'PUT' not supported
DEBUG 12640 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
DEBUG 12640 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : Successfully completed request
DEBUG 12640 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing PUT request for [/error]
DEBUG 12640 --- [io-8080-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
DEBUG 12640 --- [io-8080-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
DEBUG 12640 --- [io-8080-exec-10] o.s.web.cors.DefaultCorsProcessor        : Skip CORS processing: request is from same origin
DEBUG 12640 --- [io-8080-exec-10] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [{timestamp=Wed Sep 14 20:17:20 CEST 2016, status=405, error=Method Not Allowed, exception=org.springframework.web.HttpRequestMethodNotSupportedException, message=Request method 'PUT' not supported, path=/carwash/add}] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@78b6776e]
DEBUG 12640 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
DEBUG 12640 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : Successfully completed request

另一个有趣的问题,如果将ajax请求类型替换为POST,则该异常将有所不同:/carwash/add parsererror SyntaxError: Unexpected token < in JSON at position 0

POST请求的春季启动日志:

DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/login]
DEBUG 13348 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /login
DEBUG 13348 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public java.lang.String biz.controllers.mvc.LoginController.login()]
DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/login] is: -1
DEBUG 13348 --- [nio-8080-exec-2] o.s.w.servlet.view.BeanNameViewResolver  : No matching bean found for view name 'login'
DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Rendering view [org.springframework.web.servlet.view.InternalResourceView: name 'login'; URL [WEB-INF/pages/login.html]] in DispatcherServlet with name 'dispatcherServlet'
DEBUG 13348 --- [nio-8080-exec-2] o.s.w.servlet.view.InternalResourceView  : Forwarding to resource [WEB-INF/pages/login.html] in InternalResourceView 'login'
DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/WEB-INF/pages/login.html]
DEBUG 13348 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /WEB-INF/pages/login.html
DEBUG 13348 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/WEB-INF/pages/login.html]
DEBUG 13348 --- [nio-8080-exec-2] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/WEB-INF/pages/login.html] are [/**]
DEBUG 13348 --- [nio-8080-exec-2] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/WEB-INF/pages/login.html] are {}
DEBUG 13348 --- [nio-8080-exec-2] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/WEB-INF/pages/login.html] to HandlerExecutionChain with handler [org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler@1aa2d29f] and 1 interceptor
DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/WEB-INF/pages/login.html] is: -1
DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Successfully completed request
DEBUG 13348 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Successfully completed request

并且当我从日志中退出时,应用程序无法No matching bean found for view name 'login'我如何拥有@RequestMapping(value ={"/", "/login"}, method = RequestMethod.GET)的控制器并且没有问题可以打开包含链接/login的页面...我想念的是什么?

“/”和“/ login”的控制器

@RequestMapping(value ={"/", "/login"}, method = RequestMethod.GET)
public String login() {
    return "login";
}

配置:

@Bean
public InternalResourceViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("WEB-INF/pages/");
    resolver.setSuffix(".html");
    return resolver;
}

1 个答案:

答案 0 :(得分:0)

要在启用CSRF的情况下发出AJAX / JSON请求,您必须将CSRF令牌作为HTTP请求标头传递,而不是参数或其他数据。

在页面上,您的元标记应如下所示:

{% if label_hidden %}
  {% if multiple %}
    <div{{ attributes }}>
      {% for item in items %}
        <div{{ item.attributes }}>{{ item.content }}</div>
      {% endfor %}
    </div>
  {% else %}
    {% for item in items %}
     {{ item.content }}   -------------> add class here
    {% endfor %}
  {% endif %}
{% else %}
  <div{{ attributes }}>
    <div{{ title_attributes }}>{{ label }}</div>
    {% if multiple %}
      <div>
    {% endif %}
    {% for item in items %}
      <div{{ item.attributes }}>{{ item.content }}</div>
    {% endfor %}
    {% if multiple %}
      </div>
    {% endif %}
  </div>
{% endif %}

然后,在JS代码中的某处准备值:

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

将CSRF令牌作为标题传递:

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

你可以在这里看到更多

http://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html