1. 什么是跨域访问
案例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时,ajax发起请求的地址如果与当前页面所在的地址违反同源策略时,则称之为跨域(请求);
2. 同源策略
说明:浏览器在发起ajax请求时,必须遵守同源策略的规定,否则数据无法正常解析.
策略说明:发起请求时,必须满足 协议://域名:端口 都相同(和当前页面对比)时,满足同源策略要求,浏览器可以正确的发起请求,并且解析结果.
但是如果上述的三项中有一项不同,则表示跨域访问,浏览器不予解析返回值结果.
3. 实现跨域访问
3.1 JSONP
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的
3.1.2 JSONP跨域实现原理
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":"Tom"})
3.1.3 jQuery实现JSONP跨域访问
<!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/JSON.html
$.ajax({
url:"http://manage.jt.com/web/testJSONP",
type:"get", //jsonp只能支持get请求,不支持post,因为src不支持post提交
dataType:"jsonp", //dataType表示返回值类型,如果是跨越访问,则必须添加jsonp
jsonp: "callback", //指定参数名称,一般默认约定都叫callback
jsonpCallback: "getJSONP", //指定回调函数名称
success:function (data){ //data经过jQuery封装返回就是json串
alert(data.id);
alert(data.password);
//转化为字符串使用
//var obj = eval("("+data+")");
//alert(obj.name);
}
});
})
</script>
</head>
<body>
<h1>JSON跨域请求测试</h1>
</body>
</html>
3.1.4 页面分析
3.1.5 编辑JSONPController
说明:在jt-manage 中添加JSONPController实现跨域访问
@RestController
public class JSONPController {
/**
http://manage.jt.com/web/testJSONP?callback=getJSONP&_=1595324880197
* JSONPObject 专门负责封装JOSNP的返回值结果
* 注意事项: 返回值结果必须通过特殊格式封装 callback(JSON数据)
* @return
*/
@RequestMapping("/web/testJSONP")
public JSONPObject jsonp(String callback) {
//准备返回数据
User user = new User();
user.setId(100L).setPassword("我是密码");
return new JSONPObject(callback, user);
}
}
3.2 CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,通过添加请求头信息,将源地址进行标识,之后发往后端服务器.
实现CORS通信的关键点: 跨域请求由浏览器和服务器共同完成,要求双方都必须统一跨域才行,但是默认的条件下服务器端是不允许跨域的,所以必须经过配置才行.
3.2.1 CORS 实现跨域入门
- 在common项目中添加跨域配置
//类似于web项目中使用的web.xml配置文件
@Configuration
public class CorsConfig implements WebMvcConfigurer {
/**
* 配置后端服务器可以跨域的清单
* 参数说明; addMapping : 什么样的请求可以进行跨域 /web/** /aa/*
* /* 匹配一级目录
* /** 匹配多级目录
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://www.jt.com") //配置源
.allowedMethods("GET","POST","PUT") //允许的请求方式
.allowCredentials(true) //是否允许携带cookie
.maxAge(1800); //允许跨域的持续时间
}
}
- 编辑ajax请求页面
<!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.username);
})
})
</script>
</head>
<body>
<h1>JSON跨域请求测试</h1>
</body>
</html>
- 编辑CorsController
@RestController
public class CorsController {
@RequestMapping("/web/testCors")
public User testUser() {
return new User().setId(10L).setUsername("Cors");
}
}
- 查看响应信息
4.通过CORS完成用户信息校验
4.1 业务需求
- 说明: 当用户添加注册信息时,需要向jt-sso 单点登录系统进行数据的校验,如果数据库中存在/不存在都应该返回相关信息,之后页面提示信息给用户.
4.2 页面JS分析
- 注意事项:明确url中的哪些部分一定写死在js中,之后根据检索的功能快速定位JS位置
- 说明: 配置ajax事项JSONP的请求
4.3 业务接口文档
4.4 编辑UserController
/**
* 1.url ; http://sso.jt.com/user/check/{param}/{type}
* 2. 参数:param 需要校验的数据
* type 校验的类型
* 3.返回结果 : SysResult对象 data:true/false
* 4.jsonp跨域访问
* 终极目标: 1.快 省 安全
* @param param
* @param type
* @return
*/
@RequestMapping("/check/{param}/{type}")
public JSONPObject checUser (@PathVariable String param,@PathVariable Integer type,String callbake){
boolean flag = userService.checkUser(param,type); //要求返回true/false
SysResult sysResult = SysResult.success(flag);
return new JSONPObeject(callback,sysResult);
}
4.5 编辑UserService实现类
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
//如果项目中固定写死的key通过static方式维护
Map<Integer,String> paramMap;
static{
Map<Integer,String> map = new HashMap<>();
map.put(1,"username");
map.put(2,"phone");
map.put(3,"email");
paramMap = map;
}
// 校验数据库中是否有数据
//type为类型,可选参数1 username、2 phone、3 email
@Override
public boolean checkUser(String param, Integer type) {
if (param==null || param=="") {
throw new IllegalArgumentException("参数不能为空!");
}
//1.将type类型转化为具体的字段信息
String column = paramMap.get(type);
//2.通过获取数据库中的记录总数,判断是否存在数据
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(column,param);
int count = userMapper.selectCount(queryWrapper);
return count>0?true:false;
}
}
4.6 业务呈现效果
4.7 优化页面结构
- 添加状态码的判断编辑jdValidate.js
4.8 全局异常处理机制优化
//标识改类是全局异常处理机制的配置类
@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.getParamter("callbakc");
if(StringUtils.isEmpty(callback){ ///不是跨域访问
log.error("{"+exeption.getMessage()+"}",exception); //输出日志
return SysResult.fail();
}
String method = request.getMethod();
if(!method.equalsIgnoreCase("GET"){ //不是get请求,说明不是跨域
log.error("{"+exeption.getMessage()+"}",exception); //输出日志
return SysResult.fail();
}
//标识进行JSONP的请求,按照JSONP的方式返回数据
log.error("{"+exeption.getMessage()+"}",exception); //输出日志
return new JSONPObject(callback,SysResult.fall());
}