先後端分離下的跨域問題

問題產生的緣由

  先後端分離項目中,前端和後臺服務可能沒有部署在一臺服務器上。這樣的話,先後端ip就會不一致,那麼就會產生跨域,每每先後端項目部署的端口一般也可能會不同,這樣也會產生跨域問題。再就是使用的域名不一致也會產生這樣的問題。html

 

錯誤信息

Failed to load http://192.168.2.111:8080/login: No 'Access-Control-Allow-Origin' header ispresent on the requested resource. Origin 'http://192.168.2.110:8084' is therefore not allowed access. The response had HTTP status code 405.

反正差很少就是上面這樣的報錯。前端

 

前端實現

前端可使用jsonp的方式解決這個問題,但只能針對get方法。java

 

Node也有開源的組件http-proxy-middleware能夠支持代理。nginx

var express=require('express');
var proxy=require('http-proxy-middleware');
var app=express();
app.use('/api',proxy({target:'http://192.168.2.110:8080', changeOrigin:true}));
app.listen(3000);

 

 

Nginx實現

設置反向代理web

#user  nobody;
worker_processes  1;
 
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
 
#pid        logs/nginx.pid;
 
events {
    worker_connections  1024;
}
 
http {
    include       mime.types;
    default_type  application/octet-stream;
 
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
 
    #access_log  logs/access.log  main;
 
    sendfile        on;
    #tcp_nopush     on;
 
    #keepalive_timeout  0;
    keepalive_timeout  65;
 
    #gzip  on;
 
    upstream appstore-web {
        server 192.168.2.110:8080;
    }
    
 server { listen 80; server_name 192.168.2.111; client_max_body_size 1000M; #charset koi8-r; #access_log logs/host.access.log main; location / { root /jyzh/appstore-front/dist; index index.html; } location /appstore-web { proxy_pass http://appstore-web; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
 
    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;
 
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
 
    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;
 
    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
 
    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;
 
    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;
 
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
 
}

 

後端實現

過濾器

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 全局跨域過濾器 * * @author wzm * @version 1.0.0 * @date 2020/1/25 **/
public class CrosFilter implements Filter { public static final Logger LOGGER = LoggerFactory.getLogger(CrosFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) servletResponse; HttpServletRequest req = (HttpServletRequest)servletRequest; LOGGER.info("****執行跨域攔截****",req.getRequestURI()); //*號表示對全部請求都容許跨域訪問
        res.addHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization"); res.addHeader("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS"); res.setHeader("Access-Control-Max-Age", "3600"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }

 

 

springboot配置

import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; /** * 全局跨域 * * @author wzm * @version 1.0.0 * @date 2020/1/25 **/ @Configuration public class WebMvcConfiguration extends WebMvcConfigurationSupport { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST", "DELETE", "PATCH") .allowCredentials(true) .maxAge(3600); } }

 

終極解決辦法(CORS)

  簡單來講,CORS是一種訪問機制,英文全稱是Cross-Origin Resource Sharing,即咱們常說的跨域資源共享,經過在服務器端設置響應頭,把發起跨域的原始域名添加到Access-Control-Allow-Origin 便可。spring

  這種方式幾乎適用於全部場景。express


 

Request Headers(請求頭)json

 

Origin後端

  表示跨域請求的原始域。api

    

Access-Control-Request-Method

  表示跨域請求的方式。(如GET/POST)

    

Access-Control-Request-Headers

  表示跨域請求的請求頭信息。

 


 

Response headers(響應頭 )

 

Access-Control-Allow-Origin

  表示容許哪些原始域進行跨域訪問。(字符數組)

    

Access-Control-Allow-Credentials

  表示是否容許客戶端獲取用戶憑據。(布爾類型)

    

從瀏覽器發起跨域請求,而且要附帶Cookie信息給服務器。則必須具有兩個條件:

1. 瀏覽器端:發送AJAX請求前需設置通訊對象XHR的withCredentials 屬性爲true。

2. 服務器端:設置Access-Control-Allow-Credentials爲true。兩個條件缺一不可,不然即便服務器贊成發送Cookie,瀏覽器也沒法獲取。

 

Access-Control-Allow-Methods

  表示跨域請求的方式的容許範圍。(例如只受權GET/POST)

 

Access-Control-Allow-Headers

  表示跨域請求的頭部的容許範圍。

 

Access-Control-Expose-Headers

  表示暴露哪些頭部信息,並提供給客戶端。(由於基於安全考慮,若是沒有設置額外的暴露,跨域的通訊對象XMLHttpRequest只能獲取標準的頭部信息)

 

Access-Control-Max-Age

  表示預檢請求 [Preflight Request] 的最大緩存時間。

 


 

CorsFilter

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 全局跨域 * * @author wzm * @version 1.0.0 * @date 2020/1/25 **/ @Configuration public class GlobalCorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //放行哪些原始域
        config.addAllowedOrigin("*"); //是否發送Cookie信息
        config.setAllowCredentials(true); //放行哪些原始域(請求方式)
        config.addAllowedMethod("*"); //放行哪些原始域(頭部信息)
        config.addAllowedHeader("*"); //暴露哪些頭部信息(由於跨域訪問默認不能獲取所有頭部信息)
        config.addExposedHeader("*"); //添加映射路徑
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config); //返回新的CorsFilter
        return new CorsFilter(configSource); } }

 

WebMvcConfigurer

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 全局跨域 * * @author wzm * @version 1.0.0 * @date 2020/1/25 **/ @Configuration public class GlobalCorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() {   @Override   //重寫父類提供的跨域請求處理的接口
            public void addCorsMappings(CorsRegistry registry) {   //添加映射路徑
                registry.addMapping("/**")   //放行哪些原始域
                        .allowedOrigins("*")   //是否發送Cookie信息
                        .allowCredentials(true)   //放行哪些原始域(請求方式)
                        .allowedMethods("GET","POST", "PUT", "DELETE")   //放行哪些原始域(頭部信息)
                        .allowedHeaders("*")   //暴露哪些頭部信息(由於跨域訪問默認不能獲取所有頭部信息)
                        .exposedHeaders("Token", "Channel"); } }; } }
 

CrossOrigin註解

一、在方法上(@RequestMapping)使用註解 @CrossOrigin

@RequestMapping("/hello") @ResponseBody @CrossOrigin("http://192.168.2.110:8080") public String hello( ){ return "hello"; }

 

二、在控制器(@Controller)上使用註解 @CrossOrigin

@Controller @CrossOrigin(origins = "http://192.168.2.110:8080", maxAge = 3600) public class HelloController{ @RequestMapping("/hello") @ResponseBody public String hello( ){ return "hello"; } }
相關文章
相關標籤/搜索