定义: 当浏览器解析ajax时,ajax发起请求的地址如果与当前页面所在的地址违反同源策略时,则称之为跨域(请求)
案例1:
页面网址:http://manage.jt.com:80/test.html
ajax请求: http://manage.jt.com:80/test.json
结论: 当请求协议://域名:port端口号都相同时 访问正常的.
案例2:
页面网址: http://www.jt.com:80/test.html
ajax请求: http://manage.jt.com:80/test.json
结论: 当浏览器的地址与ajax地址不同时,请求不能正常执行.
说明:浏览器在发起AJAX请求时,必须遵守同源策略的规定.否则数据无法正常解析.
策略说明: 发起请求时,必须满足 协议://域名:端口都相同(和当前页面对比)时.满足同源策略要求.浏览器可以正确的发起请求,并且解析结果.,
但是如果上述的三项中有一项不同,则表示跨域访问.浏览器不予解析返回值结果.
例题: 问http://manage.jt.com/test.html 和http://localhost:8091/test.json 能否通信?
答案: 不能通信 1)域名不一致 2)端口不同
1).利用javascript中的src属性实现跨域
<script type="text/javascript" src="http://manage.jt.com/test.json"></script>
2).自定义回调函数 function callback(){}
function hello(data){
alert(data.name);
}
3).将返回值结果 进行特殊的格式封装 callback(JSON数据)
hello({"id":"1","name":"tomcat猫"})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSONP测试</title>
<script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
alert("测试访问开始!!!!!")
//网页位置: www.jt.com/JSONP.html
$.ajax({
url:"http://manage.jt.com/web/testJSONP",
type:"get", //jsonp只能支持get请求 不支持post,因为src支持get
dataType:"jsonp", //dataType表示返回值类型 如果是跨域访问,则必须添加jsonp
//jsonp: "callback", //指定参数名称
//jsonpCallback: "hello", //指定回调函数名称
success:function (data){ //data经过jQuery封装返回就是json串
alert(data.id);
alert(data.name);
//转化为字符串使用
//var obj = eval("("+data+")");
//alert(obj.name);
}
});
})
</script>
</head>
<body>
<h1>JSON跨域请求测试</h1>
</body>
</html>
说明: 在jt-manage中添加JSONPController.实现跨域访问
@RestController
public class JSONPController {
/**
* url:http://manage.jt.com/web/testJSONP?callback=jQuery111107990405330439474_1595323762313&_=1595323762314
* @return JSONPObject 专门负责封装JSONP的返回值结果的.
* 注意事项: 返回值结果必须通过特殊的格式封装 callback(JSON数据)
*/
@RequestMapping("/web/testJSONP")
public JSONPObject jsonp(String callback) {
//准备返回数据
User user = new User();
user.setId(100L).setPassword("我是密码");
return new JSONPObject(callback, user);
}
}
说明:当下的主流的浏览器天生都支持跨域,通过添加请求头信息,将源地址进行标识,之后发往后端服务器.
关键点: 跨域请求由浏览器和服务器共同完成,.要求双方都必须同意跨域才行. 但是默认的条件下服务器端是不允许跨域的.所以必须经过配置才行
说明:在jt-common中添加跨域配置
//类似于web项目中使用的web.xml配置文件
@Configuration
public class CorsConfig implements WebMvcConfigurer{
/**
* 配置后端服务器可以跨域的清单
* 参数说明: addMapping:什么样的请求可以进行跨域 /web/** /aaa/b/c/e/d/d/d
* /* 匹配一级目录
* /** 匹配多级目录 使用最多
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") //配置源 通配
.allowedMethods("GET","POST","PUT","DELETE","HEAD") //允许的请求方式
.allowCredentials(true) //是否允许携带cookie
.maxAge(1800); //允许跨域的持续时间
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试JSON跨域问题</title>
<script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script>
<script type="text/javascript">
/*$(){}结构必然是jQuery函数类库导入后才能正确执行*/
$(function(){ //正常
alert("我要进行cors的跨域了!!!!"); //正常
//利用jQuery发起AJAX请求
$.get("http://manage.jt.com/web/testCors",function(data){
alert(data.id);
alert(data.password);
})
})
</script>
</head>
<body>
<h1>JSON跨域请求测试</h1>
</body>
</html>
@RestController
public class CorsController {
@RequestMapping("/web/testCors")
public User testUser() {
System.out.println("我执行了业务操作!!!");
return new User().setId(100L).setPassword("我是cors的返回值!!!!");
}
}
说明:当用户添加注册信息时,需要向JT-SSO单点登录系统进行数据的校验.如果数据库中存在/不存在都应该返回相关信息,之后页面提示信息给用户.
注意事项: 明确url中的哪些部分一定写死在js中.之后根据检索的功能快速定位JS位置.
说明:配置ajax实现JSONP的请求.
/**
* 1.url:http://sso.jt.com/user/check/{param}/{type}
* 2.参数:param 需要校验的数据
* type 校验的类型
* 3.返回值结果: SysResult对象 data:true/false
* 4.jsonp跨域访问
* 终极目标: 1.快 省 安全
*/
@RequestMapping("/check/{param}/{type}")
public JSONPObject checkUser(@PathVariable String param,@PathVariable Integer type,
String callback) {
boolean flag = userService.checkUser(param,type);//要求返回true/false
SysResult sysResult = SysResult.success(flag);
return new JSONPObject(callback, sysResult);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
private static Map<Integer,String> paramMap; //如果项目中固定写死的可以通过static方式维护
static {
//1.将type类型转化为具体的字段信息.
Map<Integer,String> map = new HashMap<>();
map.put(1, "username");
map.put(2, "phone");
map.put(3, "email");
paramMap = map;
}
//校验数据库中是否有数据!!! 有true 没有false
//type 1 username, 2 phone ,3 eamil
@Override
public boolean checkUser(String param, Integer type) {
//String column = type==1?"username":((type==2)?"phone":"email");
String column = paramMap.get(type);
//1.通过获取数据库中的记录总数,判断是否存在数据.
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(column, param);
int count = userMapper.selectCount(queryWrapper);
return count>0?true:false;
}
}
添加状态码的判断编辑jdValidate.js:
$.ajax({
url : "http://sso.jt.com/user/check/"+escape(pin)+"/1?r=" + Math.random(),
dataType : "jsonp",
success : function(data) {
checkpin = data.data?"1":"0";
//校验服务器数据是否正确
if(data.status == 200){
//返回值data.data=true 已存在/false 不存在
if (!data.data) {
validateSettings.succeed.run(option);
namestate = true;
}else {
validateSettings.error.run(option, "该用户名已占用!");
namestate = false;
}
}else{
validateSettings.error.run(option, "服务器正忙,请重试!!!");
namestate = false;
}
}
});
package com.jt.aop;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.jt.vo.SysResult;
import lombok.extern.slf4j.Slf4j;
//标识改类是全局异常处理机制的配置类
@RestControllerAdvice //advice通知 返回的数据都是json串
@Slf4j //添加日志
public class SystemExceptionAOP {
/*
* 添加通用异常返回的方法.
* 底层原理:AOP的异常通知.
*
* 常规手段: SysResult.fail();
* 跨域访问: JSONP 必须满足JSONP跨域访问要求 callback(SysResult.fail())
* 问题: 如何断定用户使用的是跨域方式还是普通业务调用???
* 分析: jsonp请求 get请求的方式携带?callback
* 判断依据: 用户参数是否携带callback 特定参数,一般条件下不会使用
* */
@ExceptionHandler({RuntimeException.class}) //拦截运行时异常
public Object systemResultException(HttpServletRequest request,Exception exception) {
String callback = request.getParameter("callback");
if(StringUtils.isEmpty(callback)) { //不是跨域访问
log.error("{~~~~~~"+exception.getMessage()+"}", exception); //输出日志
return SysResult.fail(); //返回统一的失败数据
}
//说明:有可能跨域 jsonp只能提交GET请求
String method = request.getMethod();
if(!method.equalsIgnoreCase("GET")) {
//如果不是get请求,不是jsonp请求方式
log.error("{~~~~~~"+exception.getMessage()+"}", exception); //输出日志
return SysResult.fail(); //返回统一的失败数据
}
//3.如果程序执行到这里,标识进行了JSONP的请求. 按照JSONP的方式返回数据
log.error("{~~~~~~"+exception.getMessage()+"}", exception); //输出日志
return new JSONPObject(callback, SysResult.fail());
}
}
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。现在HttpClient最新版本为 HttpClient 4.5 .6(2015-09-11)
说明:跨域的形式只能发生在有页面的web端,由浏览器解析页面JS.发起ajax请求.实现跨域.但是由于分布式的思想,后端服务器的数量众多.有时可能由A业务服务器向B业务服务器获取业务数据,但是没有页面的支持,所以不能通过跨域的形式实现.只能通过远程过程调用的方式实现数据的通信.
<!--添加httpClient jar包 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
package com.jt.test;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test;
public class TestHttpClient {
/**
* 案例说明: 万能用法
* 1.利用httpClient机制 访问百度服务器. http://www.baidu.com
* 2.实现步骤: (了解即可)
* 1.定义请求网址
* 2.定义httpClient工具API对象
* 3.定义请求的类型
* 4.发起请求,获取返回值结果
* 5.校验返回值
* 6.获取返回值结果数据.
* @throws IOException
* @throws ClientProtocolException
*/
@Test
public void testGet() throws ClientProtocolException, IOException {
String url = "http://www.baidu.com";//任意网络资源 包裹业务服务器.
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpClient.execute(httpGet);//不能保证网络请求一定正确.
//发起请求之后 需要判断返回值结果是否正确 一般条件下判断响应状态码信息是否为200.
//404 400 提交参数异常 406 接收参数异常 500 服务器异常 504 超时 200正常
int status = response.getStatusLine().getStatusCode();
if(status == 200) {
//说明请求正确 获取返回值的实体对象
HttpEntity httpEntity = response.getEntity();
//将远程服务器返回的信息,转化为字符串. 方便调用 1.json 2.html代码片段
String result = EntityUtils.toString(httpEntity,"utf-8");
System.out.println(result);
}
}
}