我正在PHP Lumen中构建一个应用程序,它在登录时返回一个令牌。我不知道如何超越这个。
我应该如何使用这些令牌维护会话?
具体来说,如果我使用reactjs或vanilla HTML / CSS / jQuery,如何在我为Web应用程序的安全部分发出的每个请求中发送它们,如何在客户端存储令牌?
答案 0 :(得分:11)
我通常做的是将令牌保留在本地存储中,这样即使用户离开网站,我也可以保留令牌。
await Task.WhenAll(..)
每次用户加载页面时,我要做的第一件事就是寻找令牌的存在。
localStorage.setItem('app-token', theTokenFromServer);
如果使用react,我将令牌保持在全局状态(例如使用redux):
token = localStorage.getItem('app-token');
使用vanilla javascript我会将其保留在我的连接实用程序中。这可能类似于以下内容:
function loadAppToken(token) {
return {
type: 'LOAD_TOKEN',
payload: { token },
};
}
我在反应应用程序中仍然有一个fetch实用程序,类似于前面的代码,但我会在选项中发送令牌,方法是在每个请求的redux中间件中获取它。 / p>
答案 1 :(得分:5)
我们假设您要使用。
构建APP构建REST API时必须忘记会话。
REST API是无状态的,因此它们不能依赖会话,它们必须仅使用客户端提供的数据处理请求。
所有客户想要做的只是交换一些username
& password
代币。
这是一个示例HTTP请求
POST /api/v1/authentication HTTP/1.1
Host: localhost
Content-Type: application/json
{
"username": "foo",
"password": "bar"
}
响应是:
{
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
我们的API如何处理身份验证请求?
它会检查用户名为foo
&的用户密码bar
已成立且在DB
它将生成一个JWT(Json Web Token)
它将返回包含JWT
这是一些超级简单的auth方法,仅举例来说。
public function authAction()
{
/** Get your payload somehow */
$request = $_POST;
//Validate if username & password are given/
$user = $this->model->auth($username, $password);
if(!$user) {
//throw error for not valid credentials
}
$jwt = $this->jwt->create($user);
//return response with $jwt
}
如你所见,他们没有会话或其他任何内容。
我们的客户端如何处理回复?
客户端可以使用某些包superagent来处理请求&以这种方式回复我们的API,过程将简化为:
let data = {
username: email,
password: password
};
request
.post('/api/v1/authentication')
.set('Content-Type', 'application/json')
.send(data)
.end(function (error, response) {
//response.body.token
});
您可以使用一些3RD PT软件包来生成和验证 JWT,而不是自己编写。
看看这个package,你可以看到它是如何完成的。
记住要始终创建强大的签名。
我建议使用RSA keys
我不是在宣传或支持这个项目,只是觉得在这里分享它很有用。我从未使用它,我在NodeJS项目中使用类似的东西。
你知道localStorage
& cookies
对我来说,我正在使用cookies,因为:
但这完全取决于你。
从现在开始,每次请求到服务器都必须包含您的JWT。
在REST API中,您必须编写一个方法来验证JWT并将其交换为用户对象。
请求示例:
let jwt = ...; //GET IT FROM LOCALSTORAGE OR COOKIE
request
.get('/api/v1/posts')
.set('Content-Type', 'application/json')
.set('Authorization', jwt)
.end(function (error, response) {
});
API将如何处理此请求
public function postsAction()
{
$jwt = $this->headers->get('Authorization');
if(!$this->jwt->validate($jwt)) {
//throw unauthorized error
}
$user = $this->model->exchangeJWT($jwt);
//Your logic here
}
如果您使用cookie来保存JWT,请注意设置过期日期。
Cookie过期日期必须等于JWT过期日期。
答案 2 :(得分:3)
目前正在使用流明的API处理相同类型的应用程序。在Lumen with JWT中执行基于令牌的身份验证的3个步骤:
<强> 1。创建令牌并在登录成功后返回
public function login(Request $request) {
$token = $this->jwt->attempt(['user_name' => $data['user_name'], 'password' => $data['password']]); //$token = $this->jwt->attempt($data);
if (!$token) {
$response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_INVALID_USER, 'error' => array(Messages::MSG_INVALID_USER)));
return response()->json($response);
} else {
$user = \Auth::setToken($token)->user();
$data = array('token' => $token,'user_id' => $user->id);
$response = array('success' => true, 'data' => $data, 'detail' => array('message' => Messages::MSG_SUCCESS, 'error' => null));
return response()->json($response);
}
}
<强> 2。定义用于令牌验证的中间件
public function handle($request, Closure $next, $guard = null) {
try {
$token = $request->header('X-TOKEN');
$user_id = $request->header('X-USER');
$user = \Auth::setToken($token)->user();
if ($user && $user->id == $user_id) {
return $next($request);
} else {
$response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERR_INVALID_TOKEN, 'error' => Messages::MSG_ERR_INVALID_TOKEN));
return response()->json($response);
}
} catch (Exception $ex) {
$response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERROR_500, 'error' => array($ex)));
return response()->json($response);
}
}
第3。将令牌存储在localstorage或cookies中
localStorage.setItem("Token", JSON.stringify(TokenData));
TokenData = JSON.parse(localStorage.getItem("Token"));
或
$.cookie('Token', JSON.stringify(TokenData), {expires: 1, path: '/'});
TokenData = JSON.parse($.cookie("Token"));
<强> 4。发送包含标题中每个请求的令牌
使用自定义标题的请求
$.ajax({
url: 'foo/bar',
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
每个请求的标头
$.ajaxSetup({
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
希望它会有所帮助。
注意:在从localstorage
或cookies
读取数据时添加一些检查和数据验证。
答案 3 :(得分:2)
您可以将其存储在浏览器的localStorage中,然后在每个请求到服务器的标题中设置它。
答案 4 :(得分:2)
对于加密和解密,您可以在内置laravel的加密模型
中使用
使用Illuminate \ Support \ Facades \ Crypt;
我们为生成API令牌所做的工作将需要一系列必填字段。
让我们创建数据
$data = [
'user_id' => $user->id,
'time_stemp' => \Carbon::now() // Carbon is laravel's time model(class) for managing times
'expire_on' => \Carbon::now()->addDays(2); //here i'm setting token expires time for 2 days you can change any
];
$data = serialize($data);
然后使用Crypt加密您的数据
$accessToken = Crypt::encrypt($data);
现在发送到前端作为回应并保存在本地存储或cookie中,此处不需要时间的任何内容仅在服务器上进行检查。
现在在每个请求传递该令牌并在服务器端创建一个将解析您的数据的中间件,如果您的令牌时间少于到期时间,则向前移动,否则发送错误403或任何您想要的东西。
如何解析服务器端的数据
使用命令创建中间件: php artisan make:中间件ApiAuth 然后处理部分
//Accesstoken you passed in $headers or in $request param use whatever you like
$searilizerData = Crypt::decrypt($headers['AccessToken']);
$data = unserialize($searilizerData);
//check if expire_on is less then current server time
if($data['expire_on] <= \Curbon::now()){
next(); // let them contuine and access data
} else {
throw new Exception ("Your token has expired please regenerate your token",403);
}
希望这会有所帮助:)
答案 5 :(得分:0)
我会写下快速的todo和最佳做法,因为有很多方法可以用代码来完成。
登录请求,通过{email,password}。
成功时,获取令牌并将其存储在本地,首选localstorage,但也可以使用cookie。
在每个页面加载您的react应用程序时,您应该对该令牌进行功能检查,它将对其进行解密,并获取详细信息以供进一步使用。
我的意思是获取用户名,用户ID等。如果您想要添加它,更重要的是“过期”,如果令牌过期,您将用户重定向到登录页面,或者您可以重新请求新令牌,它实际上取决于您的应用程序。
注销,非常简单......只需从客户端删除令牌并重定向到登录页面即可。
确保对于“经过身份验证的”页面,您检查该令牌是否存在,甚至可以检查用户类型。
**对于JWT的客户端解码,您可以使用: https://www.npmjs.com/package/jwt-client
答案 6 :(得分:0)
我最近完成了一个反应门户网站,我们使用JWT启动,维护和过期用户的会话。
代码段
假设您已从登录API调用中检索到令牌:
在会话存储和状态(redux store)中设置令牌
window.sessionStorage.setItem('partyToken', token)
store.dispatch({type: 'profile/setToken', payload: { token }})
从会话存储或状态(redux存储)中检索令牌
const token = window.sessionStorage.getItem('token')
const token = store.getState().profile && store.getState().profile.token
当然,您可以定义一个通用函数,您可以在每次API调用后设置/刷新令牌。类似于检索,因为在进行API调用之前需要令牌。