什么是跨域
跨域是指从一个域名的网页去请求另一个域名的资源,域名、端口、协议任一不同,都算作跨域。
基于安全考虑,浏览器对于javascript的同源策略的限制。
前端跨域
我们前端项目中采用MVVM框架vue.js,2.0以后尤大推荐用axios与后台交互。
功能特性
- 在浏览器中发送 XMLHttpRequests 请求
- 在 node.js 中发送 http请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 自动转换 JSON 数据
- 客户端支持保护安全免受 XSRF 攻击
vue cli脚手架搭建只需要在config目录下的index.js增加changeOrigin的配置即可
dev: {
env: require('./dev.env'),
port: 8080,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': {
target: 'http://localhost:8004',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
还需要注意的是,axios 默认是没有携带 cookie 的,如果后台项目是用 session 认证用户信息的话,每次请求的 sessionId 是不一样的。
需要在axios配置中将 withCredentials 设置为 true。
正式环境中,vue.js 代码会被 build 成 html 文件在 nginx 中部署,转发请求到后端服务。
location /api/ {
proxy_pass http://web-server/;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
后端跨域
后端主要是通过跨域资源共享(CORS) 来实现允许跨域请求,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。
在 spring boot 中有两种跨域方式,在没有启用 spring security 时,直接在请求的controller层注解,类或者方法上都可以,其中 @CrossOrigin 中的2个参数:
- origins : 允许可访问的域列表
- maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。
@CrossOrigin(origins = "http://www.baidu.com", maxAge = 3600)
@Api(tags = "数据源", description = "数据源业务类接口服务")
@RestController
@RequestMapping("/dataSource")
public class DataSourceAction {
@Resource
private DataSourceService service;
@CrossOrigin()
@ApiOperation("测试并保存数据源")
@RequestMapping(value = "/saveDataSource", method = RequestMethod.POST)
public void saveDataSource(@RequestParam Map map) throws Exception {
service.testAndSave(map);
}
}
也可以设置成全局
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author pilsy
*/
@Configuration
public class MiddlewareConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CorsMiddleware())
.addPathPatterns("/**");
}
public class CorsMiddleware extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler
) throws Exception {
if (request.getMethod().equals("OPTIONS")) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, DELETE");
response.addHeader("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,Authorization,If-Modified-Since,Cache-Control,Content-Type");
response.addHeader("Access-Control-Max-Age", "3600");
response.addHeader("charset", "utf-8");
}
return super.preHandle(request, response, handler);
}
}
}
在启用 spring security 后,由 security 管理跨域设置,首先实例化 CorsConfigurationSource
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(AllowedOrigins.getAllowedOrigin());
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("DNT", "X-Mx-ReqToken", "Keep-Alive", "User-Agent", "X-Requested-With", "Authorization", "If-Modified-Since", "Cache-Control", "Content-Type"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
再将 CorsConfigurationSource 注册到 security 配置项中
httpSecurity.cors().configurationSource(corsConfigurationSource());