分布式技术入坑指南(六)

分布式技术入坑指南(六)

15. SSO单点登陆系统搭建

模块划分:

  • sso.taotao.interface
  • sso.taotao.service
  • sso.taotao.web

两个问题:

  • 不同域名之间Cookie是不能共享的,这也意味着用户在 sso.taotao.com 系统登陆之后,跳转到 taotao.com 后记录用户登陆凭证(token)的Cookie不存在,访问某些个人信息时提示重新登陆。
  • 不同域名之间的跨域请求是不会返回数据的,比如 item.taotao.com 下一个订单需要发送一个请求到 sso.taotao.com 校验当前用户是否登陆,请求发送成功并且会被处理,但是数据是返回不到 item.taotao.com 系统的。

解决办法:

  • 设置Cookie的doMain属性
  • 以JSONP的方式请求获得数据

    Cookie跨域

    学习cookie的两个属性:

path
path属性是设置cookie存在的路径的,比如在当前目录下存入了一个cookie,那么该目录及子目录下的所有资源请求都能查看到这个cookie,但是父级目录是看不到的。

* 设置 path=“/” ,即cookie可被站点根目录下所有文件访问到。
* 设置 path=“/AAA/” ,即cookie可被站点根目录下的AAA目录下的所有文件访问到。

domain
前面实现了cookie同域之间的访问,但在分布式系统中,跨域访问频繁,怎么做到跨域访问cookie不丢失呢?
比如在 sso.taotao 登陆之后用户的token存入浏览器,想要在 order.taotao.com 系统中获取这个token,需要存放cookie的时候设置domain属性:

domain=“taotao.com”

跨域访问前提是域名之间具有相同部分,一般是二级域名之间的访问。

参考博客:https://www.cnblogs.com/st-leslie/p/5720460.html


JSONP

JSONP与JSON不是一回事
JSON是一种数据交换格式,而JSONP是一种非官方跨域数据交互协议

Ajax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<html>
<head>
<meta charset="utf-8">
<title>jsonp</title>
<script type="text/javascript" src="jquery-1.6.4.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$.ajax({
type:"get",
url:"local.js",
// url:"http://192.168.184.130:82/remote.js",
dataType:"json",
success:function(json){
alert(json.data);
}
});
});
</script>
</head>
<body></body>
</html>

local.js

1
{data:"local"}

结果是可以请求到本地json数据,但是当访问远程的remote.js时
Alt text
资源无法访问😔

值得庆幸的是:在Web页面调用Js文件却不受跨域的影响,注意:不是ajax请求,凡是通过有src属性的标签调用跨域资源都是不受影响的,比如<script>标签

既然能够远程调用js文件,那么怎么获取文件中的数据为我所用呢?其实很简单。

测试:
本地一个方法要获得远程的json数据:

1
2
3
4
5
6
7
<script type="text/javascript">
//回调方法
function method(json){
alert(json.data);
}
</script>
<script type="text/javascript" src="http://192.168.184.130:82/remote.js"></script>

remote.js

1
2
/*远程js中调用*/
method({data:"remote"})

其实就是远程的js中调用了本地所需要数据的回调方法。

Alt text

同样的道理,在Ajax请求中,跨域请求一个url,我们要求控制器返回的数据格式为:callbackMethod(jsonStr),也就是对返回的json包裹一个前台js的回调方法名称。我们需要做的:1. 告诉控制器回调方法叫什么; 2.在回调方法写获得数据后的逻辑,而控制器返回json的同时回调了该方法。

知道原理,异步请求跨域数据的方法应该这么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<meta charset="utf-8">
<title>jsonp</title>
<script type="text/javascript" src="jquery-1.6.4.js"></script>
<script type="text/javascript">
//回调方法
function callbackMethod(json){
alert(json.data);
}
//通过标签调用json数据
var url = "http://192.168.184.130:82/remote.js";
var script = document.createElement('script');
script.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body></body>
</html>

Jquery对JSONP实现了封装,自动生成了回调函数并取出数据给success方法调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<head>
<meta charset="utf-8">
<title>jsonp</title>
<script type="text/javascript" src="jquery-1.6.4.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$.ajax({
type:"get",
url:"http://192.168.184.130:82/remote.js",
dataType:"jsonp",
//下面两行可不写,意思就是 http://192.168.184.130:82/remote.js?callback=callbackMethod,不写会自动生成。
jsonp: "callback",
jsonpCallback:"callbackMethod",
success:function(json){
alert(json.data);
}
});
});
</script>
</head>
<body></body>
</html>

参考博文:
https://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
https://www.cnblogs.com/JinQuanLi/p/6551415.html


注册登陆业务实现

sso-interface

定义注册登陆的接口

  • 根据参数和类型来校验参数;
  • 注册用户;
  • 根据用户名和密码登录;
  • 根据token获取用户的信息;
  • 用户登出

sso-service

登陆逻辑:

  • 校验数据;
  • 与数据库对比,如果成功将密码设为null,要求存入redis中的用户对象不保存密码;
  • 随机生成用户Token,传给controller,用于创建cookie;
  • 模拟Session。以key=XXX:Token,value=用户对象Json字符串存入Redis,设置Redis中的key过期时间

校验用户的Token是否存在:

  • 根据token拼接Redis中的Key,查找Redis中是否存在对应的value;
  • 如果查到,返回用户对象,重新设置Redis中key的过期时间;
  • 未查到,返回错误

发布服务,以便其他系统调用

sso-web

登陆逻辑:

  • 调用服务,接收用户登陆后的Token
  • 创建Cookie(跨域),key为指定标识符,value=token,设置cookie的过期时间(模拟session)

校验用户的Token是否存在:
可能会存在其他系统跨域调用本控制器检查用户是否登陆,本系统中跨域请求使用JSONP的方式,因此方法签名应提供一个callback的参数,用来以方法名包裹返回Json的数据。

  • 判断是否是JONSP请求,如果是:调用服务检测Redis是否存在token返回检测结果,并用函数名包裹json数据;
  • 如果不是,调用服务正常返回json数据

访问认证

以拦截器形式实现

  • 依赖sso-service、sso-web工程
  • 从cookie中取token。
  • 没有token,需要跳转到登录页面。
  • 有token。调用sso系统的服务,根据token查询用户信息。
  • 如果查不到用户信息。用户登录已经过期。需要跳转到登录页面。
  • 查询到用户信息。放行。

方案一:
拦截器写在sso-web中,其他系统依赖sso-web工程,mvc配置文件配置Interceptor的类在sso-web工程中,优点是不需要每个系统都写拦截器,只需要配置,缺点是拦截条件不方便定制。

方案一:
每个系统实现自己的拦截器,调用sso-service服务进行校验用户token。


可把我累坏了🤮

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×