问题产生的原因
前后端分离项目中,前端和后台服务可能没有部署在一台服务器上。这样的话,前后端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"; } }
原文地址:https://www.cnblogs.com/jockming/p/12233215.html