今天公司让我实现一套单点登录系统,参考了一下有关资料和以往公司所做的项目,先实现一个比较简单的可扩展的单点登录系统。
首先默认有三个系统,第一系统当然是最为重要的认证系统,或者叫做认证中心。其次有两个应用系统,应用系统A和应用系统B。当然,应用系统可以扩展更多CDEFG等等。首先来看看整个系统的流程。
1. 首先用户向应用系统A发送请求,系统A会检查自己和用户的会话状态,我管这个状态叫局部会话状态。各个应用系统会维持好各自的局部会话状态,用以检查用户是否已经登录。如果检查局部状态发现用户已经登录,则不需要向认证中心发送请求,直接显示用户为登录状态。
2. 若局部状态为未登录状态,则系统A将用户请求重定向到认证中心进行认证。若是用户之前在认证中心认证过,在重定向时会同时提交之前的认证信息。若是没有认证或是认证信息会话已过期,则认证中心跳转到登录页面重新进行登录验证。在验证中心维持了一个全局的会话状态。
3. 认证成功之后,认证系统会在本地保存全局的会话状态,然后再请求重新定位到referer也就是之前的A站的连接,并附带上验证相关的信息。此时系统A将验证信息保存为局部会话状态。这样在局部会话存在期间,用户每次访问系统A就不再需要重复向认证中心提交认证请求了。
4. 用户如果再去访问应用系统B,其流程也一样,系统B发现本地没有该用户的局部会话状态,然后将用户的请求重定向的认证中心进行登录认证,之后认证系统再将认证信息转发给应用系统B,应用系统B将用户的认证信息保存为局部会话状态,这样,用户就可以实现单点登录了。
当用户推出登录的时候各个应用系统的局部状态和认证系统的全局状态也必须进行撤销。比如当用户在应用系统A退出登录,则系统B有关该用户的局部会话必须清除,认证中心的全局会话也必须清除。流程大概如下。
1.用户在应用系统A点击退出,系统A清除该用户会话信息,并向认证中心发送请求。
2.用户中心接受到请求,清除该用户的全局会话状态,并向其他应用系统主动发送请求,要求各应用系统清除本机上有关该用户的局部会话状态。此时就要求认证中心维护这一个应用系统的列表,里面记录了每个应用系统的地址的信息。
3.各应用系统接收到认证中心的请求,随机清除该用户的局部状态信息,这个用户从某个系统上退出,其他系统也同样退出了登录的状态。
其实这个单点登录系统就好比是服装店的会员卡制度一样,想象一下回到还没有互联网只有电话通信的年代。比如现在某个特别繁华的城市市开了两家gucci的品牌店。而gucci公司总部位于市中心,其他分店分别位于市区的东西四两边。若是一天,一个叫christine的美女进入东部的店,营业员tracy很热情的对这位美女说:“美女,请问您是gucci的会员吗?如果是,可以享受8折的优惠。如果不是,可以当日办理,同样可以享受到8折。”christine一听立马表示想要成为会员。但成为会员需要向总部提交申请,于是tracy打电话去向公司总部申请,电话里,christine提交了个人的身份信息,身高体重,年收入等等。接着总部那边说:“好的,christine小姐,恭喜您正式成为gucci的VIP会员,您可以立马享受到8折购物的优待。”此时,营业员tracy也将christine小姐的信息记录在店里面。christine买了一个gucci的围巾满意的回家了。又过了一个月,christine又来到东边的这家店,想买一个包包,tracy今天休息,营业员换成了sally,christine告诉sally自己是VIP会员。sally,查了一下本店的记录,christine确实是VIP会员,所以可以享受8折购物优待。可不巧christine今天忘记带钱包了,所有也没有买包包,跟sally说了声抱歉。又过了半个月christine又去了西边的一家店,告诉店员mimi她是gucci会员,mimi查看了一下本店会员表里没有christine的名字,然后告诉christine本店没有她的信息,但mimi又让christine稍等一会,她打个电话去总部问问,然后总部告诉mimi,一个半月前christine小姐已经注册过会员了,并要求mimi立即将christine的信息记录在册,这样,christine的信息也被记录在西边的店。这样,以后christine无论去西边还是东边的店买东西,都不用再向总部登记也不用店员打电话询问,因为俩家店里都记录了christine的会员信息。
这个例子其实也不够完美,很多地方也有待改进,但大概能表达个意思。大概流程说玩了,现在就具体把这个单点登录系统架设起来,并写点代码试试。
因为本人是PHPer一枚,只懂PHP,所以当然选择用PHP去实现整个系统。首先需要四台linux服务器,三台安装上php和web服务器,一台安装mysql服务器,用来存储数据,比如用户注册信息。三台中其中两台一台用作应用系统A,比如A用作商城系统的开发,应用系统B用作订餐系统。还有一台作为认证中心服务器。认证中心服务器还需要架设一台redis服务器,其实也可以不架设redis,但我很喜欢用,用它来保存用户全局会话状态。可以用redis最简单的String数据类型保存。redis设置string的语法是set name "value"的形式,类似乎memcache的 key-value。name代表用户名,value代表生成的认证码。还可以通过redis设置过期时间,超过一定时间的,将状态自动清除。好了,说了一堆文字,下面可以写点代码,用代码加文字的形式把整个单点登录流程跑一边。
比如现在我先访问的商城系统,此时我从来没有登录过任何系统。此时我浏览了一个商品点击了购买按钮,比如连接跳转的地址是http://www.shangcheng.com/buy.php查看buy.php文件。里面有一条语句是验证登录状态的:
$flag = check_login();if($flag){//如果验证通过,进入订单页面
header("order.php");}else{//如果验证失败,则到认证中心进行认证
header("http://www.auth.com/auth.php");}
?>
此时,我的浏览器重新发送请求到http://www.auth.com/auth.php,查看auth.php文件。
$username = $_COOKIE['username'];
$authkey = $_COOKIE['authkey'];
if(empty($username) && empty($authkey)){//如果都为空说明未登录过系统或cookie已过期被清除 header("login.php");}
?>
此时我应该是跳转到登录页面,然后再页面输入用户名和密码,点提交,提交到http://www.auth.com/login_check.php
$referer = $_SERVER['HTTP_REFERER'];
$username = $_POST['username'];
$password = $_POST['password'];
$mysql_server_name = '100,100,100,12';
$mysql_username = 'root';
$mysql_password = '********';
$mysql_database=db_user';
$conn=mysql_connect($mysql_server_name,$mysql_username,$mysql_password) or die("error connecting") ; //连接数据库
mysql_query("set names 'utf8'");
mysql_select_db($mysql_database); //打开数据库
$sql ="select * from tbl_users where username=".$username." password=."$password; //SQL语句
$result = mysql_query($sql,$conn); //查询
if($result){//有数据登录成功,然后登录成功之后才是重点程序.
print('登录成功');//提示登录成功
$authkey = md5($username.$userpassword.time());//认证号
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->set($username,$authkey);//将用户名和生成的认证码存入redis这就是用户的全局状态
header("refresh:3;url=".$referer."?username=".$username."&authkey=".$authkey );//将页面重新定向到之前的应用系统
}else{//登录失败
header("login_fail.html");
}然后我的浏览器重新跳转到商城系统的订单立即页面,也就是buy.php.此时buy里面还得做两件事情,第一件就是设置cookie
$username=$_GET['username'];
$authkey = $_GET['authkey'];
setcookie("username",$username);setcookie("authkey",$authkey);//第二就是启动session创建局部会话状态session_start();
$_SESSION['username'] = $username;
$_SESSION['authkey'] = $authkey;
然后下一步就进入了订单页面,这样就基本上完成了单点登录的整个过程。登录点餐系统的单点登录流程几乎一样,不必再说,退出登录也不用说了,快下班了,想休息一会儿,^^。
其实上面的程序只是我边想编写出来的,里面有很多问题和很多需要完善的地方,比如安全问题,放SQL注入,会话过期问题,还有全局会话和局部会话过期时间的统一问题。里面还有一些错误,等如果上这个项目了再慢慢完善吧,这只是一个大纲而已。毕竟一个健壮的单点登录系统本身就是一个相对复杂的系统。里面还有好多问题需要总结和发现。好累休息休息。