前后端分离下的跨域问题

Posted jockming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前后端分离下的跨域问题相关的知识,希望对你有一定的参考价值。

问题产生的原因

  前后端分离项目中,前端和后台服务可能没有部署在一台服务器上。这样的话,前后端ip就会不一致,那么就会产生跨域,往往前后端项目部署的端口通常也可能会不一样,这样也会产生跨域问题。再就是使用的域名不一致也会产生这样的问题。

 

错误信息

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方法。

 

Node也有开源的组件http-proxy-middleware可以支持代理。

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实现

设置反向代理

#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 即可。

  这种方式几乎适用于所有场景。


 

Request Headers(请求头)

 

Origin

  表示跨域请求的原始域。

    

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注解

1、在方法上(@RequestMapping)使用注解 @CrossOrigin

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

 

2、在控制器(@Controller)上使用注解 @CrossOrigin

@Controller
@CrossOrigin(origins = "http://192.168.2.110:8080", maxAge = 3600)
public class HelloController{
    @RequestMapping("/hello")
    @ResponseBody
    public String hello( ){
        return "hello";
    }
}

 

以上是关于前后端分离下的跨域问题的主要内容,如果未能解决你的问题,请参考以下文章

nodejs作为前后端分离中间件的跨域解决方案

js跨域问题前后端分离的跨域问题

前后端分离的跨域请求问题解决

Nginx部署前后端项目时的跨域问题

Spring Boot笔记-解决前后端分离在开发时的跨域问题

前后端分离的跨域介绍