• 前后端分离下的跨域问题


    问题产生的原因

      前后端分离项目中,前端和后台服务可能没有部署在一台服务器上。这样的话,前后端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";
        }
    }
  • 相关阅读:
    查看full gc频率怎么样
    【LeetCode每天一题】Linked List Cycle II(循环链表II)
    【LeetCode每天一题】Word Break()
    【LeetCode每天一题】Candy(分糖果)
    【LeetCode每天一题】Single Number II(数组中单个数字II)
    【LeetCode每天一题】Gas Station(汽油站)
    【LeetCode每天一题】Single Number(数组中单独的数字)
    【LeetCode每天一题】Sum Root to Leaf Numbers(二叉树所有根到叶节点之和)
    【LeetCode每天一题】Longest Consecutive Sequence(最长的连续序列
    【LeetCode每天一题】 Word Ladder(单词阶梯)
  • 原文地址:https://www.cnblogs.com/jockming/p/12233215.html
Copyright © 2020-2023  润新知