【问题标题】:Spring security lead to error for PUT and POST request through AjaxSpring 安全性导致通过 Ajax 的 PUT 和 POST 请求出错
【发布时间】:2017-01-22 15:43:42
【问题描述】:

我有一个小的 ajax 请求导致异常 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 请求的用户具有角色“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");
}

我所有的应用程序都基于 Spring Boot,因此我也记录了这个 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 &lt; in JSON at position 0

来自 Spring Boot 的 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;
}

【问题讨论】:

  • 请添加有@RequestMapping(value ={"/", "/login"}的方法
  • @reos 方法已添加,但问题在另一个地方,我在 spring 安全配置中添加了.csrf().disable() 并提出请求开始工作
  • 好的,那是因为你正在使用 csrf 保护。

标签: ajax spring-security spring-boot


【解决方案1】:

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

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

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

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

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

将 CSRF 令牌作为标头传递:

$.ajax({
        type: "PUT",
        async: false,
        url: '/carwash/add',
        data: [your data],
        beforeSend: function(xhr) {
            // here it is
            xhr.setRequestHeader(header, token);
        },
        success: function(obj) {
            //  ....
        },
        ....

你可以在这里看到更多

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

【讨论】:

  • 我处理了你所有的代码,但出现了这个错误:SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': '${_csrf.headerName}' is not a valid HTTP header field name
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-10
  • 1970-01-01
  • 1970-01-01
  • 2017-08-12
  • 2021-06-23
  • 2018-09-26
相关资源
最近更新 更多