这就是我刚才解决这个问题的方法。在我的示例中,我使用的是 Socket.IO,但我很确定您可以轻松地重写 Socket.IO 部分以使其也与 RachetPHP 一起使用。
套接字服务器
socket服务器依赖于文件cookie.js和array.js,节点模块express、http、socket.io、request和dotenv。我不是 cookie.js 的原作者,但是 cmets 中没有提到作者,所以我无法为此给予任何功劳,对不起。
这是启动服务器的 server.js 文件。它是一个简单的套接字服务器,用于跟踪谁当前在线。然而,有趣的部分是当服务器向 Laravel 应用程序上的socket/auth 发出 POST 请求时:
var express = require('express');
var app = express();
var server = require('http').Server(app)
var io = require('socket.io')(server);
var request = require('request');
var co = require('./cookie.js');
var array = require('./array.js');
// This loads the Laravel .env file
require('dotenv').config({path: '../.env'});
server.listen(process.env.SOCKET_SERVER_PORT);
var activeSockets = {};
var disconnectTimeouts = {};
// When a client connects
io.on('connection', function(socket)
{
console.log('Client connected...');
// Read the laravel_session cookie.
var cookieManager = new co.cookie(socket.handshake.headers.cookie);
var sess = cookieManager.get("laravel_session"); // Rename "laravel_session" to whatever you called it
// This is where the socket asks the Laravel app to authenticate the user
request.post('http://' + process.env.SOCKET_SERVER_HOST + '/socket/auth?s=' + sess, function(error, response, body)
{
try {
// Parse the response from the server
body = JSON.parse(body);
}
catch(e)
{
console.log('Error while parsing JSON', e);
error = true;
}
if ( ! error && response.statusCode == 200 && body.authenticated)
{
// Assign users ID to the socket
socket.userId = body.user.id;
if ( ! array.contains(activeSockets, socket.userId))
{
// The client is now 'active'
activeSockets.push(socket.userId);
var message = body.user.firstname + ' is now online!';
console.log(message);
// Tell everyone that the user has joined
socket.broadcast.emit('userJoined', socket.userId);
}
else if (array.hasKey(disconnectTimeouts, 'user_' + socket.userId))
{
clearTimeout(disconnectTimeouts['user_' + socket.userId]);
delete disconnectTimeouts['user_id' + socket.userId];
}
socket.on('disconnect', function()
{
// The client is 'inactive' if he doesn't reastablish the connection within 10 seconds
// For a 'who is online' list, this timeout ensures that the client does not disappear and reappear on each page reload
disconnectTimeouts['user_' + socket.userId] = setTimeout(function()
{
delete disconnectTimeouts['user_' + socket.userId];
array.remove(activeSockets, socket.userId);
var message = body.user.firstname + ' is now offline.';
console.log(message);
socket.broadcast.emit('userLeft', socket.userId);
}, 10000);
});
}
});
});
我在代码中添加了一些 cmets,所以它应该是不言自明的。请注意,我在我的 Laravel .env 文件中添加了 SOCKET_SERVER_HOST 和 SOCKET_SERVER_PORT,以便能够在不编辑代码的情况下更改主机和端口并在不同的环境中运行服务器。
SOCKET_SERVER_HOST = localhost
SOCKET_SERVER_PORT = 1337
使用 Laravel 通过会话 cookie 对用户进行身份验证
这是解析 cookie 并响应用户是否可以通过身份验证的 SocketController(JSON 响应)。它与您在your answer 中描述的机制相同。这不是在控制器中处理 cookie 解析的最佳设计,但在这种情况下应该没问题,因为控制器只处理那一件事,它的功能不会在应用程序的另一点使用。
/app/Http/Controllers/SocketController.php
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Users\UserRepositoryInterface;
use Illuminate\Auth\Guard;
use Illuminate\Database\DatabaseManager;
use Illuminate\Encryption\Encrypter;
use Illuminate\Http\Request;
use Illuminate\Routing\ResponseFactory;
/**
* Class SocketController
* @package App\Http\Controllers
*/
class SocketController extends Controller {
/**
* @var Encrypter
*/
private $encrypter;
/**
* @var DatabaseManager
*/
private $database;
/**
* @var UserRepositoryInterface
*/
private $users;
/**
* Initialize a new SocketController instance.
*
* @param Encrypter $encrypter
* @param DatabaseManager $database
* @param UserRepositoryInterface $users
*/
public function __construct(Encrypter $encrypter, DatabaseManager $database, UserRepositoryInterface $users)
{
parent::__construct();
$this->middleware('internal');
$this->encrypter = $encrypter;
$this->database = $database;
$this->users = $users;
}
/**
* Authorize a user from node.js socket server.
*
* @param Request $request
* @param ResponseFactory $response
* @param Guard $auth
* @return \Symfony\Component\HttpFoundation\Response
*/
public function authenticate(Request $request, ResponseFactory $response, Guard $auth)
{
try
{
$payload = $this->getPayload($request->get('s'));
} catch (\Exception $e)
{
return $response->json([
'authenticated' => false,
'message' => $e->getMessage()
]);
}
$user = $this->users->find($payload->{$auth->getName()});
return $response->json([
'authenticated' => true,
'user' => $user->toArray()
]);
}
/**
* Get session payload from encrypted laravel session.
*
* @param $session
* @return object
* @throws \Exception
*/
private function getPayload($session)
{
$sessionId = $this->encrypter->decrypt($session);
$sessionEntry = $this->getSession($sessionId);
$payload = base64_decode($sessionEntry->payload);
return (object) unserialize($payload);
}
/**
* Fetches base64 encoded session string from the database.
*
* @param $sessionId
* @return mixed
* @throws \Exception
*/
private function getSession($sessionId)
{
$sessionEntry = $this->database->connection()
->table('sessions')->select('*')->whereId($sessionId)->first();
if (is_null($sessionEntry))
{
throw new \Exception('The session could not be found. [Session ID: ' . $sessionId . ']');
}
return $sessionEntry;
}
}
在构造函数中你可以看到我指的是internal中间件。我添加了这个中间件,只允许套接字服务器向socket/auth 发出请求。
这是中间件的样子:
/app/Http/Middleware/InternalMiddleware.php
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\ResponseFactory;
class InternalMiddleware {
/**
* @var ResponseFactory
*/
private $response;
/**
* @param ResponseFactory $response
*/
public function __construct(ResponseFactory $response)
{
$this->response = $response;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (preg_match(env('INTERNAL_MIDDLEWARE_IP'), $request->ip()))
{
return $next($request);
}
return $this->response->make('Unauthorized', 401);
}
}
要让这个中间件工作,在内核中注册它并将INTERNAL_MIDDLEWARE_IP 属性 - 这只是一个定义允许哪些 IP 地址的正则表达式 - 到您的 .env 文件:
本地测试(任何 IP):
INTERNAL_MIDDLEWARE_IP = /^.*$/
生产环境:
INTERNAL_MIDDLEWARE_IP = /^192\.168\.0\.1$/
很抱歉,我无法帮助您解决 RachetPHP,但我认为您知道如何解决这个问题。