必备:
1.lumen5.x version > 5.5+
2.redis
3.mysql
现在我们指定:
1.用户是用手机号做账户来登录
2.用户数据都使用缓存
3.用户登录也查询缓存,缓存查询不到再去数据库查询,并更新缓存
4.Redis用hash表维护用户数据
5.用户登录后,系统颁发一个Token凭据作为用户身份识别
6.token可以放到cookie
7.更新用户在线状态
那么流程应该是这样:
WechatIMG375.jpeg
router
$router->post('login', 'User\UserController@login');
create Model
<?php
namespace App\Http\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Redis;
class User extends Model
{
public $table = 'user';
public $dateFormat = 'U';
/**
* @param array $data
* @return array
* @author mjShu
*/
public static function login(array $data):array {
$user = Redis::hget('user_name',$data['name']);
if($user && $user['password'] == $data['password']){
$user = json_decode($user,true);
}else{
$user = User::select('name','username','sign','avatar')->where([
'name'=>$data['name'],
'password'=>$data['password']
])->first();
if ($user){
Redis::hset('user_name',$user->id,json_encode($user));
$user = $user->toArray();
}else{
return [];
}
}
return $user;
}
}
create Controller
class UserController extends Controller
{
const time = 3600000;
public function login(Request $request):array
{
$data = $request->all();
if (strlen($data['name']) == 11 && strlen($data['password']) >=6) {
$data['password'] = md5($data['password']);
$user = User::login($data);
if ($user){
$token = md5($data['name'] . $request->getClientIp().time());
Redis::setex('token:'.$token,self::time,$user['id']);
return ['code'=>200,'user'=>$user,'token'=>$token];
}else{
return ['code'=>400];
}
}
}
创建表:
WechatIMG377.jpeg
插入模拟数据 name 为17612148988 password 为md5(227227)
现在我们通过POSTMAN测试下:
WechatIMG378.jpeg
检查Redis
WechatIMG379.jpeg
现在业务逻辑部分正常,现在我们来写html部分
<script src="/admin/js/jquery.js"></script>
<script src="/admin/js/jquery.cookie.js"></script>
<form >
<dl class="admin_login">
<dt>
<strong>用户登录</strong>
<em>User Login</em>
</dt>
<dd class="user_icon">
<input type="text" id='name' placeholder="账号" class="login_txtbx"/>
</dd>
<dd class="pwd_icon">
<input type="password" id='password' placeholder="密码" class="login_txtbx"/>
</dd>
<dd>
<input type="submit" value="立即登陆" class="submit_btn" />
</dd>
<dd>
<a href="/register"><input type="button" value="立即注册" class="submit_btn"/></a>
</dd>
<dd>
<p>© 2015- {{date('Y')}} 版权所有</p>
</dd>
</dl>
</form>
</body>
<script>
$('form').submit(function () {
var name = $("#name").val();
var password = $("#password").val();
if(name.length == 11 && password.length >=6){
$.post('/login',{name:name,password:password},function (data) {
console.log(data)
if(data.code == 200){
console.log(data.token);
$.cookie('token',data.token);
$.cookie('uid',data.user.id);
console.log($.cookie('token'));
//成功跳转
}else{
//失败
}
})
}
return false;
})
</script>
现在我们来试试:
WechatIMG380.jpeg
WechatIMG381.jpeg
通过console.log打印我们可以在控制台和cookie看到成功打印出我们想要的数据,cookie也写入了token
创建Middleware中间件校验用户的登录状态
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
class Login
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$token = $_COOKIE['token'];
$uid = $_COOKIE['uid'];
if($token!=''){
$tokenCache = Redis::get('token:'.$token);
if($uid == $tokenCache){
return $next($request);
}
}
return redirect('/');
}
}
修改app.php引导文件注册路由中间件
$app->routeMiddleware([
'login' => App\Http\Middleware\Login::class,
]);
新增route与校验登录令牌group
$router->group(['middleware' => 'login'],function ($router) {
$router->get('index',['as' => 'index', function () {
return view('Index.index');
}]);
创建Index目录与index.balde.php文件
<script src="/json/json2.js"></script>
<script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script>
<script src='/admin/js/jquery.js'></script>
<script src="/admin/js/jquery.cookie.js"></script>
<script>
ws = new WebSocket('ws://'+location.hostname+':8082');
ws.onopen = function (e) {
ws.send(JSON.stringify({type:0,token:$.cookie('token'),uid:$.cookie('uid')}));
}
ws.onmessage = function (e) {
console.log(e);
var data = JSON.parse(e.data);
//系统消息
switch (data.msgType){
case 1:
break;
case 2: //盒子消息
layim.showNew('Friend', true);
break;
case 3: //点对点消息
layim.getMessage(obj);
break;
case 4: //上下线状态更新
if(data.online == 0){
layim.setFriendStatus(data.uid, 'offline');
}else{
layim.setFriendStatus(data.uid, 'online');
}
break;
case 5: //群组消息
break;
case 6: //添加好友到列表
break;
case 7: //添加群组到列表
//通知服务器准备推送群组消息
ws.send(JSON.stringify({type:7,group_id:data.id}));
break;
case 8: //接受群组消息
layim.getMessage({
system: true
,id: data.group_id
,type: "group"
,content: data.content
});
break;
default:
console.log(data)
}
}
});
</script>
修改WebsocketController
public static function onMessage($client_id, $data)
{
if(!is_array($data)){
$data = json_decode($data,true);
}
switch ($data['type']){
case 0:
$status = UserController::checkSocketToken($data); //检查token合法性
if($status){
//绑定uid,更改上线状态,通知该用户所有的好友
Gateway::bindUid($client_id, $data['uid']);
Redis::set('bind:'.$client_id,$data['uid']);
GroupController::joinGroup($client_id);
}
break;
//省略......
public static function checkSocketToken(array $data):bool {
$tokenCache = Redis::get('token:'.$data['token']);
if($data['uid'] == $tokenCache){
return true;
}else{
return false;
}
}
现在访问我们的登录页面登录成功后应该调整到ws链接页面,我们打开谷歌浏览器的控制台可以发现成功连接;
WechatIMG384.jpeg
WechatIMG383.jpeg
我们来分析ws代码
//实例一个socket连接
ws = new WebSocket('ws://'+location.hostname+':8082');
//在连接建立完成后发送数据给socket,其中携带了cookie的数据,实际是让服务器校验身份合法
ws.onopen = function (e) {
ws.send(JSON.stringify({type:0,token:$.cookie('token'),uid:$.cookie('uid')}));
}
//为了保活这个链接,我们需要心跳机制, js一个定时器,每10秒执行一次,与socket服务通讯
var sh;
sh = setInterval("sendHeartbeat()", 10000);
function sendHeartbeat() {
if(ws.readyState == 1){
ws.send(JSON.stringify({type:'4'}));
}
if(ws.readyState == 3){
layer.alert('与服务器链接中断,请检查网络');
clearInterval(sh);
}











网友评论