Skip to content

Commit 465683f

Browse files
committed
Initial Commit
0 parents  commit 465683f

File tree

8 files changed

+393
-0
lines changed

8 files changed

+393
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.idea
2+
composer.lock
3+
vendor

README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# JwtSession
2+
3+
JwtSession is a PHP session replacement. Instead of use FileSystem, just use JWT TOKEN.
4+
The implementation following the SessionHandlerInterface.
5+
6+
## How to use:
7+
8+
Before the session_start() use the command:
9+
10+
```php
11+
<?php
12+
$handler = new \ByJG\Session\JwtSession('your.domain.com', 'your super secret key');
13+
session_set_save_handler($handler, true);
14+
```
15+
16+
Now, all your `$_SESSION` variable will be saved directly to a JWT Token!!
17+
18+
## Motivation
19+
20+
The default PHP Session does not work in different servers using round robin or other algorithms.
21+
This occurs because PHP Session are saved by default in the file system.
22+
23+
There are implementations can save the session to REDIS or MEMCACHED, for example.
24+
But this requires to you create a new server to store this session and creates a single point of failure.
25+
To avoid this you have to create REDIS/MEMCACHED clusters.
26+
27+
But if you save the session into JWT Token you do not need to create a new server.
28+
Just to use.
29+
30+
## Security Information
31+
32+
The JWT Token cannot be changed, but it can be read.
33+
This implementation save the JWT into a client cookie.
34+
Because of this _**do not** store in the JWT Token sensible data like passwords_.
35+
36+
## Install
37+
38+
```
39+
composer require "byjg/jwt-session=1.0.*"
40+
```
41+
42+
## Customizations
43+
44+
### Setting the validity of JWT Token
45+
46+
```php
47+
<?php
48+
// Setting to 50 minutes
49+
$handler = new \ByJG\Session\JwtSession('your.domain.com', 'your super secret key', 50);
50+
session_set_save_handler($handler, true);
51+
```
52+
53+
### Setting the different Session Contexts
54+
55+
```php
56+
<?php
57+
$handler = new \ByJG\Session\JwtSession('your.domain.com', 'your super secret key', 20, 'MYCONTEXT');
58+
session_set_save_handler($handler, true);
59+
```
60+
61+
### Create the handler and replace the session handler
62+
63+
```php
64+
<?php
65+
$handler = new \ByJG\Session\JwtSession('your.domain.com', 'your super secret key');
66+
$handler->replaceSessionHandler(true);
67+
```

composer.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "byjg/jwt-session",
3+
"description": "Use JWT Token as a PHP Session",
4+
"authors": [
5+
{
6+
"name": "João Gilberto Magalhães",
7+
"email": "joao@byjg.com.br"
8+
}
9+
],
10+
"autoload": {
11+
"psr-4": {
12+
"ByJG\\Session\\": "src/"
13+
}
14+
},
15+
"minimum-stability": "dev",
16+
"prefer-stable": true,
17+
"require": {
18+
"php": ">=5.6.0",
19+
"byjg/jwt-wrapper": "1.0.*"
20+
},
21+
"license": "MIT"
22+
}

src/JwtSession.php

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
<?php
2+
/**
3+
* User: jg
4+
* Date: 14/02/17
5+
* Time: 12:52
6+
*/
7+
8+
namespace ByJG\Session;
9+
10+
use ByJG\Util\JwtWrapper;
11+
use SessionHandlerInterface;
12+
13+
class JwtSession implements SessionHandlerInterface
14+
{
15+
const COOKIE_PREFIX = "AUTH_BEARER_";
16+
17+
protected $serverName;
18+
19+
protected $secretKey;
20+
21+
protected $timeOutMinutes;
22+
23+
protected $suffix = "default";
24+
25+
/**
26+
* JwtSession constructor.
27+
*
28+
* @param $serverName
29+
* @param $secretKey
30+
* @param int $timeOutMinutes
31+
*/
32+
public function __construct($serverName, $secretKey, $timeOutMinutes = 20, $sessionContext = 'default')
33+
{
34+
$this->serverName = $serverName;
35+
$this->secretKey = $secretKey;
36+
$this->timeOutMinutes = $timeOutMinutes;
37+
$this->suffix = $sessionContext;
38+
}
39+
40+
public function replaceSessionHandler($startSession = true)
41+
{
42+
if (session_status() != PHP_SESSION_NONE) {
43+
throw new \Exception('Session already started!');
44+
}
45+
46+
session_set_save_handler($this, true);
47+
48+
if ($startSession) {
49+
ob_start();
50+
session_start();
51+
}
52+
}
53+
54+
/**
55+
* Close the session
56+
*
57+
* @link http://php.net/manual/en/sessionhandlerinterface.close.php
58+
* @return bool <p>
59+
* The return value (usually TRUE on success, FALSE on failure).
60+
* Note this value is returned internally to PHP for processing.
61+
* </p>
62+
* @since 5.4.0
63+
*/
64+
public function close()
65+
{
66+
return true;
67+
}
68+
69+
/**
70+
* Destroy a session
71+
*
72+
* @link http://php.net/manual/en/sessionhandlerinterface.destroy.php
73+
* @param string $session_id The session ID being destroyed.
74+
* @return bool <p>
75+
* The return value (usually TRUE on success, FALSE on failure).
76+
* Note this value is returned internally to PHP for processing.
77+
* </p>
78+
* @since 5.4.0
79+
*/
80+
public function destroy($session_id)
81+
{
82+
setcookie(self::COOKIE_PREFIX . $this->suffix, null);
83+
return true;
84+
}
85+
86+
/**
87+
* Cleanup old sessions
88+
*
89+
* @link http://php.net/manual/en/sessionhandlerinterface.gc.php
90+
* @param int $maxlifetime <p>
91+
* Sessions that have not updated for
92+
* the last maxlifetime seconds will be removed.
93+
* </p>
94+
* @return bool <p>
95+
* The return value (usually TRUE on success, FALSE on failure).
96+
* Note this value is returned internally to PHP for processing.
97+
* </p>
98+
* @since 5.4.0
99+
*/
100+
public function gc($maxlifetime)
101+
{
102+
return true;
103+
}
104+
105+
/**
106+
* Initialize session
107+
*
108+
* @link http://php.net/manual/en/sessionhandlerinterface.open.php
109+
* @param string $save_path The path where to store/retrieve the session.
110+
* @param string $name The session name.
111+
* @return bool <p>
112+
* The return value (usually TRUE on success, FALSE on failure).
113+
* Note this value is returned internally to PHP for processing.
114+
* </p>
115+
* @since 5.4.0
116+
*/
117+
public function open($save_path, $name)
118+
{
119+
return true;
120+
}
121+
122+
/**
123+
* Read session data
124+
*
125+
* @link http://php.net/manual/en/sessionhandlerinterface.read.php
126+
* @param string $session_id The session id to read data for.
127+
* @return string <p>
128+
* Returns an encoded string of the read data.
129+
* If nothing was read, it must return an empty string.
130+
* Note this value is returned internally to PHP for processing.
131+
* </p>
132+
* @since 5.4.0
133+
*/
134+
public function read($session_id)
135+
{
136+
try {
137+
if (isset($_COOKIE[self::COOKIE_PREFIX . $this->suffix])) {
138+
$jwt = new JwtWrapper($this->serverName, $this->secretKey);
139+
$data = $jwt->extractData($_COOKIE[self::COOKIE_PREFIX . $this->suffix]);
140+
141+
return $this->serializeSessionData($data->data);
142+
}
143+
return '';
144+
} catch (\Exception $ex) {
145+
return '';
146+
}
147+
}
148+
149+
/**
150+
* Write session data
151+
*
152+
* @link http://php.net/manual/en/sessionhandlerinterface.write.php
153+
* @param string $session_id The session id.
154+
* @param string $session_data <p>
155+
* The encoded session data. This data is the
156+
* result of the PHP internally encoding
157+
* the $_SESSION superglobal to a serialized
158+
* string and passing it as this parameter.
159+
* Please note sessions use an alternative serialization method.
160+
* </p>
161+
* @return bool <p>
162+
* The return value (usually TRUE on success, FALSE on failure).
163+
* Note this value is returned internally to PHP for processing.
164+
* </p>
165+
* @since 5.4.0
166+
*/
167+
public function write($session_id, $session_data)
168+
{
169+
$jwt = new JwtWrapper($this->serverName, $this->secretKey);
170+
$data = $jwt->createJwtData($this->unSerializeSessionData($session_data), $this->timeOutMinutes * 60);
171+
$token = $jwt->generateToken($data);
172+
173+
setcookie(self::COOKIE_PREFIX . $this->suffix, $token);
174+
175+
return true;
176+
}
177+
178+
public function serializeSessionData($array)
179+
{
180+
$result = '';
181+
foreach ($array as $key => $value) {
182+
$result .= $key . "|" . serialize($value);
183+
}
184+
185+
return $result;
186+
}
187+
188+
public function unSerializeSessionData($session_data)
189+
{
190+
$return_data = array();
191+
$offset = 0;
192+
while ($offset < strlen($session_data)) {
193+
if (!strstr(substr($session_data, $offset), "|")) {
194+
throw new \Exception("invalid data, remaining: " . substr($session_data, $offset));
195+
}
196+
$pos = strpos($session_data, "|", $offset);
197+
$num = $pos - $offset;
198+
$varname = substr($session_data, $offset, $num);
199+
$offset += $num + 1;
200+
$data = unserialize(substr($session_data, $offset));
201+
$return_data[$varname] = $data;
202+
$offset += strlen(serialize($data));
203+
}
204+
205+
return $return_data;
206+
}
207+
}

webtest/destroy.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
require_once __DIR__ . "/../vendor/autoload.php";
4+
5+
$handler = new \ByJG\Session\JwtSession('api.com.br', '1234567890');
6+
$handler->replaceSessionHandler(true);
7+
8+
session_destroy();
9+
?>
10+
11+
<h1>JwtSession Demo - Destroy whole session</h1>
12+
13+
<div>
14+
Now, your session is empty again.
15+
</div>
16+
17+
<div>
18+
Go back and check this page: <a href="index.php">Index</a>
19+
</div>

webtest/index.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
require_once __DIR__ . "/../vendor/autoload.php";
4+
5+
$handler = new \ByJG\Session\JwtSession('api.com.br', '1234567890');
6+
$handler->replaceSessionHandler(true);
7+
8+
?>
9+
10+
<h1>JwtSession Demo</h1>
11+
12+
<div>
13+
Here the user just use the JwtSession as the session handler.
14+
The $_SESSION handler current is:
15+
</div>
16+
17+
<div>
18+
<textarea cols="50" rows="20"><?php print_r($_SESSION);?>
19+
</textarea>
20+
</div>
21+
22+
<div>
23+
Now, play with sessions:
24+
<ul>
25+
<li><a href="setsession.php">Set a session</a></li>
26+
<li><a href="unsetsession.php">Unset a session</a></li>
27+
<li><a href="destroy.php">Destroy all session</a></li>
28+
</ul>
29+
</div>

webtest/setsession.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
require_once __DIR__ . "/../vendor/autoload.php";
4+
5+
$handler = new \ByJG\Session\JwtSession('api.com.br', '1234567890');
6+
$handler->replaceSessionHandler(true);
7+
8+
$count = intval($_SESSION['count']) + 1;
9+
10+
$_SESSION['count'] = $count;
11+
$_SESSION['setvalue_' . $count] = 'Set at date ' . date('Y-m-d H:i:s');
12+
13+
?>
14+
15+
<h1>JwtSession Demo - Set Session</h1>
16+
17+
<div>
18+
Everytime you reach this page I'll create a new key: setvalue1, setvalue2, ...
19+
</div>
20+
21+
<div>
22+
Go back and check this page: <a href="index.php">Index</a>
23+
</div>

0 commit comments

Comments
 (0)