好久没写过文章了,感觉自己这段时间在虚度,刚好自己在重构登录注册,就来写一篇吧,我记得当年刚入行的时候就有人问我cookie与session的区别,实际工作中,很多小公司的程序员也只关注增删改查,对于一些很基础的东西反而都没好好掌握。

最原始的登录实现

1
2
3
4
5
6
7
public function login(){
//验证传过来的账号密码是否跟数据库里面的用户对的上
if(账号密码通过验证){
session_start();
$_SESSION['xxx+uid'] = '用户信息';
}
}

这时候我们来访问下页面,这时候我们发现页面上竟然带上了cookie

这是为什么呢,因为php在我们使用SESSION的时候就会默认给我带上PHPSESSIDcookie给浏览器。

然后一些新手程序员怎么弄自动登录呢,就简单的这样写

1
2
3
4
5
6
7
8
9
public function autoLogin(){
session_start();
$userInfo = $_SESSION['xxx+uid'];
if(!empty($userInfo)) {
//获取用户信息成功,自动登录执行其它业务逻辑
} else {
//获取用户信息失败,跳转到登录页面
}
}

虽然说这样使用貌似没什么问题,但是我们看上面cookie的生命周期,我们发现在浏览器关闭的时候,这个cookie就失效了,所以有些人就这么坑爹的搞,貌似在他看来就是这么自动登录的,其实他们完全没理解这套机制。那么最简单的改法就是只要我们把这个cookie的生命周期弄长,让它在浏览器关闭的时候,下次打开浏览器也能活着。php自带的session_start这个方法它支持我们传一个‘cookie_lifetime’的值进去(你要设置cookie的过期时间,比如要设置到明天这个时间点,那就是当前的时间戳+一天的秒数)。这样设置以后,一个最简单的保持登录功能就完成了。

当然,我们也可以不用php自带的PHPSESSID,我们可以自定义sessionId。当然,php也很方便的为我们封装了方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public function login() {
//验证传过来的账号密码是否跟数据库里面的用户对的上
if(账号密码通过验证){
$sessionId = 'xxx+uid';
session_id($sessionId);
session_start();
$_SESSION[$sessionId] = '用户信息';
//把sessionId用cookie传给浏览器,并设置它的生命周期
setcookie('userSessionId', $sessionId, '过期时间', ‘cookie的路径’, ‘cookie的作用域’, ‘是否仅通过安全的 HTTPS 连接’, ‘仅通过HTTP 连接’);//具体的参数大家可以参考php.net上面的介绍
}
}
public function autoLogin(){
$sessionId = $_COOKIE['userSessionId'];//你刚才设置的cookie的name
session_id($sessionId);
session_start();
$userInfo = $_SESSION[$sessionId];
if(!empty($userInfo)) {
//获取用户信息成功,自动登录执行其它业务逻辑
} else {
//获取用户信息失败,跳转到登录页面
}
}

所以那些百度上的答案,seesion存在服务端,cookie存浏览器,你说它错吗,它也没错,但是它没把本质讲清楚,session就是依赖cookie这样使用的,这也是web界非常经典的使用方式。

上述例子中我没说明session的过期时间,session在服务端它也有自己的一个过期时间的,我们可以再php.ini里面修改session.gc_maxlifetime这个值来改变session的过期时间。

稍微演变后的登录

可以看到我们上面的登录,并且保持登录方式确实简单有效,但是随着业务的不断发展,很多公司服务器都是分布式负载一个项目,那么这个就会导致一个问题,默认的session是存文件的,你要多台服务器共享磁盘吗,显然很低效,那么这么多台服务器怎么共享session呢?当然session是支持其它的存储方式的,比如像想在用到的非常多的memcacheredis这种高速的内存数据库。那我们只要多台服务器用一个redis或一个redis集群,那么就实现session共享了。那为什么大费周章的用session,我们自己也可以凭借这种内存型数据库构造一种类似session的机制。这时候就有一种登录的方式演变出来了,我用redis直接存用户信息,然后cookie里面直接存一个自己按一定规则生成的用户标识,不也能实现登录+保持登录吗?或者可以再扩大一点,前端/客户端传一个凭证过来,然后我们通过自己存储的一套机制来验证,而且更易跨平台跟自己维护。

这里我用redis作为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public function login() {
if(账号密码通过验证){
$userKey = 'user_id:xxx';
$redisClient = new RedisClient();
$expireTime = 24*60*60;//缓存的过期时间
$redisClient->set($userKey,'用户信息xxx',$expireTime);
$userKeyEncrypt = openssl_encrypt($userKey,'aes-128-cbc','password');
//再把这个userKeyEncrypt传给前端,作为下次保持登录的标识
}
}
public function autoLogin() {
//假设已经获取到前端传过来的值为 35R4XMHmMKI04R9PDuJ4Tg==
$userKeyEncrypt = '35R4XMHmMKI04R9PDuJ4Tg==';
$userKey = openssl_decrypt($userKeyEncrypt,'aes-128-cbc','password');
$redisClient = new RedisClient();
//获取缓存中的用户信息
$userInfo = $redisClient->get($userKey);
if(!empty($userInfo)) {
//获取用户信息成功,自动登录执行其它业务逻辑
} else {
//获取用户信息失败,跳转到登录页面
}
}

redis

然后我们再调用自动登录方法,确实也能获取到用户的信息。可以自己去试一下,我这里只是把中心思想的实现写出来了。
当然你也可以不用redis 你也可以用其他的存储介质。