1.JSONP解决(只支持get方式)
关于跨域请求与jsonp
- 跨域:由于受到同源策略(协议、域名、端口三者必须全部相同)的影响,ajax请求会受到限制,要突破这种限制,跨域便产生了。跨域的解决方案有多种,这里不展开阐述,只是针对GET请求中的jsonp跨域解决方案做一下说明。
- jsonp,本质上jsonp不是xhr异步请求,就是请求了一个js文件,因此在chrome的network面板中的xhr标签下看不到jsonp的跨域请求,在js标签下能看到。就是利用script标签中src不受同源策略的限制,前端定义了回调函数,请求的js脚本中获取数据,并执行前端的回调函数,因此前后端需要统一定义下回调函数名
- .ajax中jsonp, .ajax对jsonp进行了封装看起来像是ajax请求。由于jsonp是针对get请求的跨域解决,因此之前的经验告诉我,即使type设置了post,在jsonp的时候,也会自动转换成get,直到有一天踩了个坑。翻看$.ajax模块的源码发现,只有去手动设置crossDomain为true,或者实际上是跨域,才会设置为get。否则还是填入的type
- jsonp的实现原理:
首先在客户端注册一个callback, 然后把callback的名字传给服务器。
此时,服务器先生成 json 数据。
然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp.
最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动
态执行回调函数)
前端代码:
/*
//简写形式,效果相同
$.getJSON("http://app.example.com/base/json.do?sid=1494&busiId=101&jsonpCallback=?",
function(data){
$("#showcontent").text("Result:"+data.result)
});
*/
$("#jsonBtn").click(function(){
$.ajax({
url:"http://localhost:8081/sso1/login/jsonp/1234455431",
type:"GET",
dataType:"jsonp",
jsonp: "callback",服务端用于接收callback调用的function名的参数,自定义的参数名
jsonpCallback:"weather_callback", //callback的function名称,自定义的函数名。
contentType:"application/json",
data:{},
success:function(data){
alert(data);
}
});
});
后端代码:
@ResponseBody
@RequestMapping(value="/jsonp/{token}",method=RequestMethod.GET)
public Object getUserByTokenJsonp(@PathVariable("token") String token, String callback){
System.out.println("进入jsonP"+":"+callback);
JsonResult result = null;
try {
result = userService.getUserByToken(token);
} catch (Exception e) {
e.printStackTrace();
result = JsonResult.build(500, e.getMessage());
}
return result;
//判断是否为jsonp调用
/*if (StringUtils.isBlank(callback)) {
return result;
} else {
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
mappingJacksonValue.setJsonpFunction(callback);
return mappingJacksonValue;
}*/
}
CORS 跨域
- cors与json的对比:
1.都能解决 Ajax直接请求普通文件存在跨域无权限访问的问题
2.JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
3.使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理
4.JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS
1.支持多域名配置的CORS Filter
1.在配置maven的pom.xml文件。
<!-- https://mvnrepository.com/artifact/com.thetransactioncompany/cors-filter -->
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.thetransactioncompany/java-property-utils -->
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>java-property-utils</artifactId>
<version>1.10</version>
</dependency>
在web.xml文件中配置过滤器。
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.allowOrigin</param-name>
<!-- 可以配置具体的过滤路径,例如 http://192.168.56.129,http://192.168.56.130 -->
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<!-- 允许的方法 -->
<param-value>GET,POST,HEAD,PUT,DELETE</param-value>
</init-param>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>Accept,Origin,X-Requested-With,Content-Type,Last-Modified</param-value>
</init-param>
<init-param>
<param-name>cors.exposedHeaders</param-name>
<param-value>Set-Cookie</param-value>
</init-param>
<init-param>
<param-name>cors.supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
可以查看源码,也是实现Filter的方法,可以自定义实现:
public class CORSFilter
/* */ implements Filter
2.简单的自定义CORSFilter / Interceptor
1.filter(过滤器)
继承OncePerRequestFilter,
public class CORSFilter extends OncePerRequestFilter{
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain arg2)
throws ServletException, IOException {
//Access-Control-Allow-Origin只能配置 或者一个域名*
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Access-Control-Max-Age", "1800");//30 min
arg2.doFilter(request, response);
}
}
然后,在web.xml文件里配置
<filter>
<filter-name>cros</filter-name>
<filter-class>com.sso2.filter.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cros</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
就可以,其中,Access-Control-Allow-Origin只能配置 或者一个域名*。
2.interceptor(拦截器)
继承HandlerInterceptor,实现preHandle即可,就是配置参数。
public class CORSIntercor implements HandlerInterceptor{
private List<String> excludedUrls;
public List<String> getExcludedUrls() {
return excludedUrls;
}
public void setExcludedUrls(List<String> excludedUrls) {
this.excludedUrls = excludedUrls;
}
/**
*
* @Description:
* 在业务处理器处理请求之前被调用 如果返回false
* 从当前的拦截器往回执行所有拦截器的afterCompletion(),
* 再退出拦截器链, 如果返回true 执行下一个拦截器,
* 直到所有的拦截器都执行完毕 再执行被拦截的Controller
* 然后进入拦截器链,
* 从最后一个拦截器往回执行所有的postHandle()
* 接着再从最后一个拦截器往回执行所有的afterCompletion()
*
* @param request
*
* @param response
*
* @return: boolean
*
* @author: SongJia
*
* @date: 2016-6-27 下午4:17:51
*
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("================preHandle start============================");
Collection<String> coll = response.getHeaderNames();
Iterator<String> it = coll.iterator();
while (it.hasNext()) {
String string = (String) it.next();
System.out.println(string);
}
System.out.println("================preHandle end============================");
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Access-Control-Max-Age", "1800");//30 min
return true;
}
// 在业务处理器处理请求执行完成后,生成视图之前执行的动作
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
/**
*
* @Description:
* 在DispatcherServlet完全处理完请求后被调用
* 当有拦截器抛出异常时,
* 会从当前拦截器往回执行所有的拦截器的afterCompletion()
*
* @param request
*
* @param response
*
* @param handler
*
* @param ex
*
* @author: SongJia
*
* @date: 2016-6-27 下午4:27:51
*
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("================afterCompletion start============================");
Collection<String> coll = response.getHeaderNames();
Iterator<String> it = coll.iterator();
while (it.hasNext()) {
String string = (String) it.next();
System.out.println(string);
}
System.out.println("================afterCompletion end============================");
}
}
然后,在spring mvc里面配置截然器:CORSIntercor
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 需排除拦截的地址 ,可以配置多个-->
<mvc:exclude-mapping path="/" />
<!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
<bean class="com.sso2.interceptor.CORSIntercor"/>
</mvc:interceptor>
</mvc:interceptors>
3.spring注解 @CrossOrigin
可以在Controller类上配置 @CrossOrigin,表名该类下的所有方法都是支持跨域,
@CrossOrigin
@Controller
@RequestMapping("/login")
public class LoginController {
@Autowired
private UserService userService;
如果配置在方法上,则是 只有该方法支持跨域。
@CrossOrigin(origins = "*", maxAge = 3600)
@ResponseBody
@RequestMapping(value="/token/{token}",method=RequestMethod.POST)
public Object getUserByToken(@PathVariable("token") String token, String callback){
JsonResult result = null;
try {
result = userService.getUserByToken(token);
} catch (Exception e) {
e.printStackTrace();
result = JsonResult.build(500, e.getMessage());
}
return result;
//判断是否为jsonp调用
/*if (StringUtils.isBlank(callback)) {
return result;
} else {
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
mappingJacksonValue.setJsonpFunction(callback);
return mappingJacksonValue;
}*/
}
4.Nginx 配置支持Ajax跨域
待定