SpringBoot:處理跨域請求

1、跨域背景

1.1 何爲跨域?

Url的通常格式: javascript

協議 + 域名(子域名 + 主域名) + 端口號 + 資源地址java

示例:git

https://www.dustyblog.cn:8080/say/Hello 是由github

https + www + dustyblog.cn + 8080 + say/Hello
組成。spring

只要協議,子域名,主域名,端口號這四項組成部分中有一項不一樣,就能夠認爲是不一樣的域,不一樣的域之間互相訪問資源,就被稱之爲跨域。

1.2 一次正常的請求

  • Controller層代碼:
@RequestMapping("/demo")
@RestController
public class CorsTestController {

    @GetMapping("/sayHello")
    public String sayHello() {
        return "hello world !";
    }
}
  • 啓動項目,測試請求

瀏覽器打開localhost:8080/demo/sayHellosegmentfault

能夠打印出「hello world」跨域

1.3 跨域測試

以Chrome爲例:
  • 打開任意網站,如:https://blog.csdn.net
  • 按F12,打開【開發者工具】,在裏面的【Console】能夠直接輸入js代碼測試;
var token= "LtSFVqKxvpS1nPARxS2lpUs2Q2IpGstidMrS8zMhNV3rT7RKnhLN6d2FFirkVEzVIeexgEHgI/PtnynGqjZlyGkJa4+zYIXxtDMoK/N+AB6wtsskYXereH3AR8kWErwIRvx+UOFveH3dgmdw1347SYjbL/ilGKX5xkoZCbfb1f0=,LZkg22zbNsUoHAgAUapeBn541X5OHUK7rLVNHsHWDM/BA4DCIP1f/3Bnu4GAElQU6cds/0fg9Li5cSPHe8pyhr1Ii/TNcUYxqHMf9bHyD6ugwOFTfvlmtp6RDopVrpG24RSjJbWy2kUOOjjk5uv6FUTmbrSTVoBEzAXYKZMM2m4=,R4QeD2psvrTr8tkBTjnnfUBw+YR4di+GToGjWYeR7qZk9hldUVLlZUsEEPWjtBpz+UURVmplIn5WM9Ge29ft5aS4oKDdPlIH8kWNIs9Y3r9TgH3MnSUTGrgayaNniY9Ji5wNZiZ9cE2CFzlxoyuZxOcSVfOxUw70ty0ukLVM/78=";
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8080/demo/sayHello');
xhr.setRequestHeader("x-access-token",token);
xhr.send(null);
xhr.onload = function(e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}
  • 輸入完後直接按回車鍵就能夠返回結果:
Access to XMLHttpRequest at 'http://127.0.0.1:8080/demo/sayHello' 
from origin 'https://blog.csdn.net' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

該結果代表:該請求在https://blog.csdn.net域名下請求失敗!瀏覽器

2、解決方案 - Cors跨域

2.1 Cors是什麼

CORS全稱爲 Cross Origin Resource Sharing(跨域資源共享), 每個頁面須要返回一個名爲 Access-Control-Allow-Origin的http頭來容許外域的站點訪問,你能夠僅僅暴露有限的資源和有限的外域站點訪問。

咱們能夠理解爲:若是一個請求須要容許跨域訪問,則須要在http頭中設置Access-Control-Allow-Origin來決定須要容許哪些站點來訪問。如假設須要容許https://www.dustyblog.c這個站點的請求跨域,則能夠設置:緩存

Access-Control-Allow-Origin:https://www.dustyblog.cn。springboot

2.2 方案一:使用@CrossOrigin註解

2.2.1 在Controller上使用@CrossOrigin註解

該類下的全部接口均可以經過跨域訪問
@RequestMapping("/demo2")
@RestController
//@CrossOrigin //全部域名都可訪問該類下全部接口
@CrossOrigin("https://blog.csdn.net") // 只有指定域名能夠訪問該類下全部接口
public class CorsTest2Controller {

    @GetMapping("/sayHello")
    public String sayHello() {
        return "hello world --- 2";
    }
}

這裏指定當前的CorsTest2Controller中全部的方法能夠處理https://csdn.net域上的請求,這裏能夠測試一下:

  • https://blog.csdn.net頁面打開調試窗口,輸入(注意:這裏請求地址是/demo2,請區別於1.2 案例中的/demo)
var token= "LtSFVqKxvpS1nPARxS2lpUs2Q2IpGstidMrS8zMhNV3rT7RKnhLN6d2FFirkVEzVIeexgEHgI/PtnynGqjZlyGkJa4+zYIXxtDMoK/N+AB6wtsskYXereH3AR8kWErwIRvx+UOFveH3dgmdw1347SYjbL/ilGKX5xkoZCbfb1f0=,LZkg22zbNsUoHAgAUapeBn541X5OHUK7rLVNHsHWDM/BA4DCIP1f/3Bnu4GAElQU6cds/0fg9Li5cSPHe8pyhr1Ii/TNcUYxqHMf9bHyD6ugwOFTfvlmtp6RDopVrpG24RSjJbWy2kUOOjjk5uv6FUTmbrSTVoBEzAXYKZMM2m4=,R4QeD2psvrTr8tkBTjnnfUBw+YR4di+GToGjWYeR7qZk9hldUVLlZUsEEPWjtBpz+UURVmplIn5WM9Ge29ft5aS4oKDdPlIH8kWNIs9Y3r9TgH3MnSUTGrgayaNniY9Ji5wNZiZ9cE2CFzlxoyuZxOcSVfOxUw70ty0ukLVM/78=";
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8080/demo2/sayHello');
xhr.setRequestHeader("x-access-token",token);
xhr.send(null);
xhr.onload = function(e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}

返回結果:

ƒ (e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}
VM156:8 hello world --- 2

說明跨域成功!

  • 換個域名測試一下看跨域是否還有效,在https://www.baidu.com按照上述方法測試一下,返回結果:
OPTIONS http://127.0.0.1:8080/demo2/sayHello 403
(anonymous)
Access to XMLHttpRequest at 'http://127.0.0.1:8080/demo2/sayHello' 
from origin 'http://www.cnblogs.com' has been blocked by CORS policy: 
Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

說明跨域失敗!證實該方案成功指定了部分域名能跨域!

2.3 方案二:CORS全局配置-實現WebMvcConfigurer

  • 新建跨域配置類:CorsConfig.java :
/**
 * 跨域配置
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Bean
    public WebMvcConfigurer corsConfigurer()
    {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").
                        allowedOrigins("https://www.dustyblog.cn"). //容許跨域的域名,能夠用*表示容許任何域名使用
                        allowedMethods("*"). //容許任何方法(post、get等)
                        allowedHeaders("*"). //容許任何請求頭
                        allowCredentials(true). //帶上cookie信息
                        exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L); //maxAge(3600)代表在3600秒內,不須要再發送預檢驗請求,能夠緩存該結果
            }
        };
    }
}
var token= "LtSFVqKxvpS1nPARxS2lpUs2Q2IpGstidMrS8zMhNV3rT7RKnhLN6d2FFirkVEzVIeexgEHgI/PtnynGqjZlyGkJa4+zYIXxtDMoK/N+AB6wtsskYXereH3AR8kWErwIRvx+UOFveH3dgmdw1347SYjbL/ilGKX5xkoZCbfb1f0=,LZkg22zbNsUoHAgAUapeBn541X5OHUK7rLVNHsHWDM/BA4DCIP1f/3Bnu4GAElQU6cds/0fg9Li5cSPHe8pyhr1Ii/TNcUYxqHMf9bHyD6ugwOFTfvlmtp6RDopVrpG24RSjJbWy2kUOOjjk5uv6FUTmbrSTVoBEzAXYKZMM2m4=,R4QeD2psvrTr8tkBTjnnfUBw+YR4di+GToGjWYeR7qZk9hldUVLlZUsEEPWjtBpz+UURVmplIn5WM9Ge29ft5aS4oKDdPlIH8kWNIs9Y3r9TgH3MnSUTGrgayaNniY9Ji5wNZiZ9cE2CFzlxoyuZxOcSVfOxUw70ty0ukLVM/78=";
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8080/demo3/sayHello');
xhr.setRequestHeader("x-access-token",token);
xhr.send(null);
xhr.onload = function(e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}

輸出結果

ƒ (e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}
VM433:8 hello world --- 3

說明跨域成功,換個網址如https://www.baidu.com測試依舊出現須要跨域的錯誤提示,證實該配置正確,該方案測試經過。

2.3 攔截器實現

經過實現Fiter接口在請求中添加一些Header來解決跨域的問題

@Component
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Credentials", "true");
        res.addHeader("Access-Control-Allow-Origin", "*");
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
        if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {
            response.getWriter().println("ok");
            return;
        }
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

3、更多

3.1 源碼地址

Github 示例代碼

3.2 更多文章

  1. 風塵博客
  2. 風塵博客-博客園
  3. 風塵博客-CSDN
  4. 風塵博客-SegmentFault

3.3 技術交流

風塵博客公衆號:

風塵博客

相關文章
相關標籤/搜索