欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Security框架:如何使用CorsFilter解决前端跨域请求问题

程序员文章站 2022-03-10 20:36:08
目录项目情况cors介绍解决方案项目情况最近做的pmdb项目是前后端分离的, 由于测试的时候是前端与后端联调,所以出现了跨域请求的问题。浏览器默认会向后端发送一个options方式的请求,根据后端的响...

项目情况

最近做的pmdb项目是前后端分离的, 由于测试的时候是前端与后端联调,所以出现了跨域请求的问题。

浏览器默认会向后端发送一个options方式的请求,根据后端的响应来判断后端支持哪些请求方式,支持才会真正的发送请求。

cors介绍

cors(cross-origin resource sharing 跨源资源共享),当一个请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。

在日常的项目开发时会不可避免的需要进行跨域操作,而在实际进行跨域请求时,经常会遇到类似 no 'access-control-allow-origin' header is present on the requested resource.这样的报错。

Security框架:如何使用CorsFilter解决前端跨域请求问题

这样的错误,一般是由于cors跨域验证机制设置不正确导致的。

解决方案

注释:本项目使用的是sprintboot+security+jwt+swagger

第一步

新建corsfilter,在过滤器中设置相关请求头

package com.handlecar.basf_pmdb_service.filter; 
import org.springframework.web.filter.onceperrequestfilter; 
import javax.servlet.*;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
 
public class corsfilter extends onceperrequestfilter { 
//public class corsfilter implements filter {
//    static final string origin = "origin"; 
    protected void dofilterinternal(
            httpservletrequest request, httpservletresponse response,
            filterchain filterchain) throws servletexception, ioexception { 
//        string origin = request.getheader(origin); 
        response.setheader("access-control-allow-origin", "*");//* or origin as u prefer
        response.setheader("access-control-allow-credentials", "true");
        response.setheader("access-control-allow-methods", "put, post, get, options, delete");
        response.setheader("access-control-max-age", "3600");
//        response.setheader("access-control-allow-headers", "content-type, authorization");
        response.setheader("access-control-allow-headers", "origin, no-cache, x-requested-with, if-modified-since, pragma, last-modified, cache-control, expires, content-type, x-e4m-with, authorization");
        response.setheader("xdomainrequestallowed","1");
        //使前端能够获取到
        response.setheader("access-control-expose-headers","download-status,download-filename,download-message");
 
 
        if (request.getmethod().equals("options"))
//            response.setstatus(httpservletresponse.sc_ok);
            response.setstatus(httpservletresponse.sc_no_content);
        else
            filterchain.dofilter(request, response);
 
    }
 
//    @override
//    public void dofilter(servletrequest req, servletresponse res,
//                         filterchain chain) throws ioexception, servletexception {
//
//        httpservletresponse response = (httpservletresponse) res;
//        //测试环境用【*】匹配,上生产环境后需要切换为实际的前端请求地址
//        response.setheader("access-control-allow-origin", "*");
//        response.setheader("access-control-allow-methods", "post, get, options, delete");
//
//        response.setheader("access-control-max-age", "0");
//
//        response.setheader("access-control-allow-headers", "origin, no-cache, x-requested-with, if-modified-since, pragma, last-modified, cache-control, expires, content-type, x-e4m-with, auth");
//
//        response.setheader("access-control-allow-credentials", "true");
//
//        response.setheader("xdomainrequestallowed","1");
//        chain.dofilter(req, res);
//    }
//
//    @override
//    public void destroy() {
//    }
//
//    @override
//    public void init(filterconfig arg0) throws servletexception {
//    }
}

注释:这里的access-control-expose-headers的请求头是为了使前端能够获得到后端在response中自定义的header,不设置的话,前端只能看到几个默认显示的header。我这里是在使用response导出excel的时候将文件名和下载状态信息以自定义请求头的形式放在了response的header里。

第二步

在security的配置文件中初始化corsfilter的bean

 @bean
    public corsfilter corsfilter() throws exception {
        return new corsfilter();
    }

第三步

在security的配置文件中添加filter配置,和映射配置

.antmatchers(httpmethod.options,"/**").permitall()
                    // 除上面外的所有请求全部需要鉴权认证。 .and() 相当于标示一个标签的结束,之前相当于都是一个标签项下的内容
                .anyrequest().authenticated().and()
                .addfilterbefore(corsfilter(), usernamepasswordauthenticationfilter.class)

附:该配置文件

package com.handlecar.basf_pmdb_service.conf;
import com.handlecar.basf_pmdb_service.filter.corsfilter;
import com.handlecar.basf_pmdb_service.filter.jwtauthenticationtokenfilter;
import com.handlecar.basf_pmdb_service.security.jwttokenutil;
import com.handlecar.basf_pmdb_service.security.customauthenticationprovider;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.http.httpmethod;
import org.springframework.security.config.annotation.authentication.builders.authenticationmanagerbuilder;
import org.springframework.security.config.annotation.method.configuration.enableglobalmethodsecurity;
import org.springframework.security.config.annotation.web.builders.httpsecurity;
import org.springframework.security.config.annotation.web.configuration.enablewebsecurity;
import org.springframework.security.config.annotation.web.configuration.websecurityconfigureradapter;
import org.springframework.security.config.http.sessioncreationpolicy;
import org.springframework.security.web.authentication.usernamepasswordauthenticationfilter;
//import com.allcom.security.jwttokenutil;
 
@configuration
//@enablewebsecurity is used to enable spring security's web security support and provide the spring mvc integration
@enablewebsecurity
@enableglobalmethodsecurity(prepostenabled = true)
public class websecurityconfig extends websecurityconfigureradapter {
 
 
    private final customauthenticationprovider customauthenticationprovider;
 
    @autowired
    public websecurityconfig(customauthenticationprovider customauthenticationprovider) {
        this.customauthenticationprovider = customauthenticationprovider;
    }
 
    @override
    protected void configure(authenticationmanagerbuilder auth) {
        auth.authenticationprovider(customauthenticationprovider);
    }
 
    @bean
    public jwttokenutil jwttokenutil(){
        return new jwttokenutil();
    }
 
    @bean
    public corsfilter corsfilter() throws exception {
        return new corsfilter();
    }
 
    @bean
    public jwtauthenticationtokenfilter authenticationtokenfilterbean() {
        return new jwtauthenticationtokenfilter();
    }
 
    @override
    protected void configure(httpsecurity httpsecurity) throws exception {
        httpsecurity
                // 由于使用的是jwt,我们这里不需要csrf,不用担心csrf攻击
                .csrf().disable()
                // 基于token,所以不需要session
                .sessionmanagement().sessioncreationpolicy(sessioncreationpolicy.stateless).and() 
                .authorizerequests()
                    //.antmatchers(httpmethod.options, "/**").permitall() 
                    // 允许对于网站静态资源的无授权访问
                    .antmatchers(
                        httpmethod.get,
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/webjars/springfox-swagger-ui/images/**","/swagger-resources/configuration/*","/swagger-resources",//swagger请求
                        "/v2/api-docs"
                    ).permitall()
                    // 对于获取token的rest api要允许匿名访问
                    .antmatchers("/pmdbservice/auth/**","/pmdbservice/keywords/export3").permitall()
                    .antmatchers(httpmethod.options,"/**").permitall()
                    // 除上面外的所有请求全部需要鉴权认证。 .and() 相当于标示一个标签的结束,之前相当于都是一个标签项下的内容
                .anyrequest().authenticated().and()
                .addfilterbefore(corsfilter(), usernamepasswordauthenticationfilter.class)
                .addfilterbefore(authenticationtokenfilterbean(), usernamepasswordauthenticationfilter.class);
 
        // 禁用缓存
        httpsecurity.headers().cachecontrol();
    } 
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。