1.数据表设计
用户通用信息表
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `nickname` varchar(32) NOT NULL, `avatar` varchar(128) DEFAULT NULL, `logintime` int(10) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8
用户登入信息表
CREATE TABLE `user_auths` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `identity_type` varchar(32) NOT NULL COMMENT '登入类型normal(手机号,邮箱,用户名)或(微信)weixin或(微博)weibo或(QQ)qq', `identifier` varchar(56) NOT NULL COMMENT '登入标识(phoneNumber,email,username)或(微信UserName)或(微博UID)', `credential` varchar(42) NOT NULL COMMENT '密码凭证access_token', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8
2.如何开发
第一步:用户登入成功后刷新授权表(user_autos),并将新生成的密码凭证保存至客户端;
第二步:添加拦截器,除登入请求、退出请求以及静态资源的请求不需要验证外,其他的请求都需要将客户端的密码凭证和服务器的密码凭证进行对比;
第三步:根据匹配结果,如果密码凭证不一致,执行退出操作,并提示该账号已在其他地方登入。
本文用ThinkPHP5.1框架做演示:
用户登入页面Index.php
<?php namespace app\demo\controller; class Index extends Com { public function login() { //测试数据,如果已建库,可以添加至数据表再写入session $this->recorder->write('user_id',9527); $this->recorder->write('user_name','华安'); $this->recorder->write('access_token',md5('opassword')); $this->success('登入成功','/index/index'); return '登入首页'; } public function index() { return '成功登入首页'; } }
配置的存储和读取Recorder.php
<?php namespace app\demo\controller; use think\facade\Session; class Recorder { private static $data; public function __construct() { if(!Session::has('user_info')){ self::$data = array(); }else{ self::$data = Session::get('user_info'); } } public function write($name,$value) { self::$data[$name] = $value; } public function read($name) { if(empty(self::$data[$name])){ return null; }else{ return self::$data[$name]; } } public function delete($name) { unset(self::$data[$name]); } public function __destruct() { Session::set('user_info',self::$data); // TODO: Implement __destruct() method. } }
拦截器Com.php
<?php namespace app\demo\controller; use think\App; use think\Controller; use think\facade\Session; class Com extends Controller { protected $recorder; public function __construct(App $app = null) { $this->recorder = new Recorder(); parent::__construct($app); } protected function initialize() { $controller = $this->request->controller(); $action = $this->request->action(); //添加拦截器 if($controller.$action !== 'Indexlogin'){ //验证是否登录 if(!$this->check_login()){ $this->error('请先登录','/index/login',3); } // $auth = db('user_auths')->where(['user_id'=>$this->recorder->read('user_id')])->find(); //测试数据,登入方式可以自行选择,这里采用用户名的登入方式 $auth = ['user_id'=>9527,'identity_type'=>'username','identifier'=>'华安','credential'=>md5('upassword')]; if(empty($this->recorder->read('access_token'))){ //token已失效,需要重新登入 $this->redirect('/index/login'); } if($auth['credential']!==$this->recorder->read('access_token')){ $this->error('你的账号已在别处登入','/index/login'); } } } public function check_login() { $is_login = false; if(Session::has('user_info')){ $is_login = true; } return $is_login; } }
路由配置route.php
return [ '__domain__' => [ 'demo' => 'demo', ]
3.小结
本文将用户信息部分和逻辑部分分开处理,方便拓展,适用于多种登入方式。用户信息表可以随时增加任意字段,以满足前端各种业务需求。
注意:请不要将一个账号只能一个人登入这种说法和单点登入混淆开来。