一、为什么会有跨域问题
出现跨域的根本原因:浏览器的同源策略不允许非同源的URL之间进行资源的交互。
那么,什么是同源?概念:如果两个页面的协议、域名和端口都相同,则这两个页面具有相同的源。
不同源,就属于跨域。
二、跨域解决方案
一般来讲,有JSONP和CORS两种方式,JSONP兼容性好,但是只支持GET数据请求,不支持POST请求;CORS不兼容某些低版本的浏览器但是它支持GET和POST请求。
此外,因为httpclient不依赖浏览器,所以也可以做为一种解决方案。
1、JSONP
JSONP的实现原理:由于浏览器收同源策略的限制,网页无法通过Ajax请求非同源的接口数据,但是script标签不受浏览器同源策略的影响,可以通过src属性请求非同源js脚本。简而言之,JSONP的实现原理就是通过<script>标签的src属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据。
这里以Jquery中的方式为例:
<body>
<script>
$(function() {
$.ajax({
url: 'http://www.test.top:8001/api/jsonp?name=xiaoming&age=18',
dataType: 'jsonp',
success: function(res) {
console.log(res);
}
})
})
</script>
</body>
如果想自定义参数及回调函数,代码如下:
<body>
<script>
function cb() {
console.log('111');
}
</script>
<script>
$(function() {
$.ajax({
url: 'http://www.test.top:8001/api/jsonp?name=xiaoming&age=18',
dataType: 'jsonp',
jsonpCallback: 'cb',
success: function(res) {
console.log(res);
}
})
})
</script>
</body>
2、CORS
下面给出了若干解决方案,但是本质都是一样的,都是围绕HTTP 头字段(Access-Control-Allow-Origin)展开的。
(1)新建配置类,通过CorsFilter来实现
package com.zy.demo.controller;
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;
@Configuration
public class CorseConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}
(2)新建配置类,实现WebMvcConfigurer接口,在其addCorsMappings()方法中添加映射路径、允许源、请求方法和允许头信息等设置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain1.com", "http://domain2.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
}
}
(3)使用@CrossOrigin注解方式
package com.zy.demo.controller;
import com.zy.demo.entity.custom.UserLoginParam;
import com.zy.demo.entity.table.TUser;
import com.zy.demo.service.TUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private TUserService tUserService;
@CrossOrigin(origins = "*")
@RequestMapping("/login")
public Object loginUser(UserLoginParam cUser){
Map<String, Object> result = new HashMap<String, Object>();
System.out.println(cUser);
if("admin".equals(cUser.getCUsername())&&"123456".equals(cUser.getCPwd())){
result.put("code",200);
result.put("msg","登录成功");
result.put("token","admin");
return result;
}
result.put("code",500);
result.put("msg","登录失败");
return result;
}
}
(4)添加过滤器
package com.zy.demo.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(filterName = "CORSFilter", urlPatterns = {"/*"})
@Order(value = 1)
@Configuration
public class AccessControlAllowOriginFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, response);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}
(5)Nginx代理方式,使用add_header指令,该指令可以用来添加一些头信息
Access-Control-Allow-Origin: 直译过来是允许跨域访问的源地址信息,可以配置多个源的地址(多个源用逗号分隔),也可以使用 *代表所有源
Access-Control-Allow-Methods:直译过来是允许跨域访问的请求方式,值可以为 GET POST PUT DELETE...,可以全部设置,也可以根据需要设置,多个用逗号分隔
(6)使用Gateway网关
Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其 不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监 控/埋点、限流等。
添加配置类:
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
3、httpclient方式,不经过浏览器
http原生请求:
public class HttpTest {
@Test
public void test1() throws Exception {
String url = "https://www.badu.com";
URL url1 = new URL(url);
//url连接
URLConnection urlConnection = url1.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection;
//获取httpURLConnection输入流
InputStream is = httpURLConnection.getInputStream();
//转换为字符串
InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(reader);
String line;
//将字符串一行一行读取出来
while ((line = br.readLine())!= null){
System.out.println(line);
}
}
}
实际项目使用中一般会进行封装。
结语:几种方式介绍完毕,可能还有其他方式,原理都差不多,大家下去可以自己研究一下。