405 方法不允许,弹簧启动,但我使用了 csrf 令牌头

Posted

技术标签:

【中文标题】405 方法不允许,弹簧启动,但我使用了 csrf 令牌头【英文标题】:405 Method Not Allowed, spring boot, but i used csrf token header 【发布时间】:2021-03-14 00:26:34 【问题描述】:

我正在使用 Spring Boot 制作 Web 应用程序服务器。 在开发初期,我使用了http.csrf().disable()的设置,网页运行良好。 但是自从禁用设置被删除后,错误只发生在host address page。当我请求 GET 时,它完全可以正常工作,但是,它不适用于 POST、PUT、DELETE...当我在本地主机上进行测试时,所有请求都可以正常工作,包括这些方法。

我已经使用了 csrf 令牌标头、表单的隐藏 csrf 输入以及在 ajax 中设置 csrf 标头的beforeSend。我什至更改了 nginx 设置 (error_page 405 =200 $uri;),但它保持不变。 这是我的环境。

jdk 11 春天 2.3.5 AWS EC2 - Amazon Linux 2 AMI AWS RDS - PostgreSQL 13 AWS S3 Nginx 1.18.0 毕业 5.6.4 百里香

请问有人可以帮我吗?

My Github Repository Link

# aws server- nohup.out
[ec2-user@teamcoder-webservice ~]$ vim ~/app/step3/nohup.out
2020-12-01 23:24:18.953  INFO 3697 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3ad85136, org.springframework.security.web.context.SecurityContextPersistenceFilter@2bc426f0, org.springframework.security.web.header.HeaderWriterFilter@176f7f3b, org.springframework.security.web.csrf.CsrfFilter@7d3c09ec, org.springframework.security.web.authentication.logout.LogoutFilter@1f53481b, org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter@7f93dd4e, org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter@5ad5be4a, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@33425811, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4cb2918c, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@737d100a, org.springframework.security.web.session.SessionManagementFilter@58740366, org.springframework.security.web.access.ExceptionTranslationFilter@4af45442, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@671c4166]
2020-12-01 23:24:20.068  INFO 3697 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2020-12-01 23:24:20.550  INFO 3697 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ''
2020-12-01 23:24:20.552  INFO 3697 --- [           main] DeferredRepositoryInitializationListener : Triggering deferred initialization of Spring Data repositories…
2020-12-01 23:24:21.474  INFO 3697 --- [           main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-12-01 23:24:21.499  INFO 3697 --- [           main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2020-12-01 23:24:21.527  INFO 3697 --- [           main] c.j.team.teamcoder.TeamCoderApplication  : Started TeamCoderApplication in 17.806 seconds (JVM running for 19.458)
2020-12-01 23:24:23.438  INFO 3697 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-12-01 23:24:23.443  INFO 3697 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-12-01 23:24:23.460  INFO 3697 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 17 ms
2020-12-02 03:05:22.223  WARN 3697 --- [nio-8081-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
# /etc/nginx/nginx.conf

http 
...
 server 
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
        include /etc/nginx/conf.d/service-url.inc;

        location / 
                limit_except GET POST PUT DELETE 
                        deny all;
                
                proxy_pass $service_url;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
        
        error_page 405  =200 $uri;

        error_page 404 /404.html;
            location = /40x.html 
        

        error_page 500 502 503 504 /50x.html;
            location = /50x.html 
        
    

#after change upper configuration

[ec2-user@teamcoder-webservice nginx]$ sudo /usr/sbin/nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

另外,以下代码是java spring boot代码。

<!--ajax non-used request in "user_info.html"-->
<form th:action="@'/api/v1/user/pic/' + $user.id" method="post" enctype="multipart/form-data" name="pictureForm">
    <table>
        <tr><td><label th:for="picture">Profile Image</label></td>
            <td><input type="file" id="picture" name="picture" accept="image/png, image/jpeg" /></td></tr>
        <tr><td><img th:src="@$user.picture" id="picture_img" ></td>
            <td><input type="button" name="Upload" value="Upload" onclick="submit_file_form(document.forms['pictureForm'].picture, 'pictureForm');" class="btn btn-info"/></td></tr>
    </table>
    <input type="hidden" name="pastPath" id="pastPath" th:value="$user.picture"/>
    <input type="hidden" th:name="$_csrf.parameterName" th:value="$_csrf.token" />
</form>
//ajax used request in "user_info.html" -> "userInfo.js"

...

const csrfToken = $('#_csrf').attr('content');
const csrfHeader = $('#_csrf_header').attr('content');


var user_info = 

    init : function () 
        var _this = this;

        $('#btn-update').on('click', function () 
            _this.update();
        ); 

        $('#btn-delete').on('click', function () 
            _this.delete();
        );
    ,
    update : function () 
        var data = 
            name: $('#name').val(),
            tags: commaToArray(convertTags($('#tags').val())),
            email: $('#email').val(),
            birth: $('#birth').val(),
            education: $('#education').val().toUpperCase(),
            location: $('#location').val()
        ;

        var id = $('#id').val();

        $.ajax(
            type: 'PUT', 
            url: '/api/v1/user/'+id,
            dataType: 'json',
            contentType:'application/json; charset=utf-8',
            data: JSON.stringify(data),
            beforeSend: function (xhr)
                xhr.setRequestHeader(csrfHeader, csrfToken);
            
        ).done(function() 
            alert('your information is modified');
            window.location.href = '/';
        ).fail(function (error) 
            alert(JSON.stringify(error));
        );
    ,
...
<!-- in html header -->

<meta id="_csrf" th:name="$_csrf.parameterName" th:content="$_csrf.token"/>
<meta id="_csrf_header" name="_csrf_header" th:content="$_csrf.headerName"/>
@RequiredArgsConstructor
@RestController
public class UserApiController 

    private  final UserService userService;
    private final RoleService roleService;

    @PutMapping("/api/v1/user/id")
    public Long update(@PathVariable String id,
                       @RequestBody UserUpdateRequestDto requestDto)

        roleService.reloadRolesForAuthenticatedUser(Role.USER.getKey());
        return userService.update(Long.valueOf(id), requestDto);
    


@RequiredArgsConstructor
@RestController
public class FileController 

    private final UserService userService;]
    private final S3Service s3Service;

    @PostMapping("/api/v1/user/pic/id")
    public String updateUserPic(@PathVariable String id,
                                @RequestParam("pastPath") String pastPath,
                            @RequestPart("picture") MultipartFile multipartFile) throws IOException 

        String uploadDir = s3Service.upload(pastPath, multipartFile,"users/");
        userService.updatePic(Long.valueOf(id), uploadDir);

        return "You successfully uploaded " + uploadDir + "!";
    

//SecurityConfig.java

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    private final CustomOAuth2UserService customOAuth2UserService;

    @Override
    public void configure(WebSecurity web) throws Exception 
        web.ignoring().antMatchers("/css/**", "/js/**", "/assets/img/**", "/lib/**", "/user-photos/**", "/group-files/**");
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .and()
                .authorizeRequests()  
                .antMatchers("/", "/logoption","/user/denied", "/search/**", "/privacy/rule","/profile").permitAll()  
                .antMatchers("/api/v1/user/**", "/api/v1/**").hasRole("GUEST")
                .antMatchers("/api/v1/group/**", "/group/**", "/api/v1/participate/**").hasRole("USER") 
                .anyRequest().authenticated()   
            .and()
                .logout().logoutSuccessUrl("/")
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true)
            .and()
                .exceptionHandling()
                .accessDeniedPage("/user/denied")
            .and()
                .oauth2Login()
                    .loginPage("/logoption")
                    .defaultSuccessUrl("/")
                    .userInfoEndpoint()// after oauth2 login
                        .userService(customOAuth2UserService); 

    
    
    //for Spring Security
    @Bean
    public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() 
        return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher());
    


【问题讨论】:

【参考方案1】:

尝试将这些添加到您的 SecurityConfig 中:

http.cors().and()....

以及 CORS 配置:

@Bean
public CorsFilter corsFilter() 
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");  // TODO: lock down before deploying
    config.addAllowedHeader("*");
    config.addExposedHeader(HttpHeaders.AUTHORIZATION);
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);

【讨论】:

以上是关于405 方法不允许,弹簧启动,但我使用了 csrf 令牌头的主要内容,如果未能解决你的问题,请参考以下文章

错误 405(发出 Ajax 请求时不允许使用该方法)

WSO2 EMM登录错误(错误405 - 方法不允许)

尽管以表单形式发送 CSRF 令牌,但不支持 Spring Security CSRF 405 方法 POST

django:通用类视图 + POST = HTTP 405(不允许的方法)

Axios POST 方法:405 方法不允许

Laravel 5.4 错误 405 方法不允许错误