跨域问题及解决
跨域介绍
跨域问题是浏览器为了解决与安全相关的问题,而采用的同源策略。所谓同源:URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示它们同源。
产生原因
浏览器为了保证安全,在浏览器打开的a,b,c页签下划分a,b,c三个域。正常情况下,a.html带着自己的cookie只能访问a服务器,b.html带着自己的cookie只能访问b服务器,c.html带着自己的cookie只能访问c服务器。a不能访问b、c服务器,bc也是同样不能访问除自身外的其他两个域。如果a访问b服务器,则浏览器端会报如下图错误:
解决办法
跨域是发生在前后端交互的过程中,服务与服务之间交互不存在。如果我们硬是让a跨域访问b或c服务器怎么办,可以采取以下方法:
1. 添加响应头;在b或c后端服务方法返回时添加头响应头信息。
@RestController
public class HelloWorldController {
@RequestMapping("/say")
public String sayHello(HttpServletResponse response){
response.addHeader("Access-Control-Allow-Origin","http://127.0.0.1:8080");
return "Hello Spring Boot";
}
}
注意:存在极少浏览器不支持;
2. java反向代理 ; 在a服务端做以下处理:
在配置文件添加
proxy.address=http://127.0.0.1:8081
在启动类注入RestTemplate
@SpringBootApplication
public class StApplication {
public static void main(String[] args) {
SpringApplication.run(StApplication.class, args);
}
@Autowired
private RestTemplateBuilder builder;
@Bean
public RestTemplate restTemplate(){
return builder.build();
}
}
请求类中方法
@RestController
public class TestController {
// 拦截代理地址
@Value("${proxy.address}")
private String proxyAddress;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/proxy/say")
public Object proxyHello(HttpServletRequest request){
// 处理掉代理地址中含有‘/proxy’
String url = proxyAddress + request.getRequestURI().replace("/proxy","");
return restTemplate.getForObject(url, Object.class);
}
}
注意:受后台请求方式和返回类型的影响。当然也可以不用RestTemplate ,自己书写后端方法排除这个因素。
3. nginx反向代理 ; 常用的方法。在a.html直接访问nginx,nginx做代理访问b或c服务。
前段页面a.html配置如下
<script>
$("button").click(function () {
// 此处访问nginx
$.get("http://127.0.0.1:80/proxy/say",function(data){
console.log(data);
})
})
</script>
在nginx的nginx.conf配置如下
location /proxy {
rewrite ^/proxy/(.*)$ /$1 break; #正则:去掉请求中url中的第一个proxy标识。
proxy_pass http://127.0.0.1:8081;#请求b或c服务
}
4. 利用spring boot 注解@CrossOrigin ;
直接在b或c服务方法上添加注解,只对该方法有效。
@RestController
public class HelloWorldController {
@CrossOrigin
@RequestMapping("/say")
public Student proxyHello(HttpServletResponse response){
Student student = new Student();
student.setName("张三");
return student;
}
}
在b或c服务上添加配置类,对能够扫描到包下的方法有效。
@Configuration
public class CrosConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 1,允许任何来源
corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
// 2,允许任何请求头
corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
// 3,允许任何方法
corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
// 4,允许凭证
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}
5. 利用html中的本身的script标签 ;
前端a.html中代码
<script>
function cross(data){
console.log(data);
}
</script>
<script src="http://127.0.0.1:8081/cross"></script>
后端b或c服务代码
@RestController
public class HelloWorldController {
@RequestMapping("/cross")
public String cross(HttpServletResponse response){
String str = "hello world";
return "cross('"+ str +"');";
}
}
注意:由于script 标签只支持post请求,所以该种方式对post请求有效。