我应该如何使用 PHP 和 MySQL(不使用 OAuth)以安全的方式对一组 Web API 实施“基于令牌的身份验证"?
我使用 Slim 框架在 PHP 中开发了一些 Web API,移动应用程序(iOS 和 Android)使用这些框架来处理它们的请求并获取所需的数据.
I've developed few web APIs in PHP using Slim framework which are used by mobile apps(iOS and Android) to process their requests and get the required data.
最终,在每个 API 中,我将从移动应用收到的请求发送到我网站代码库中的相应函数.然后各个函数接受请求和请求参数,处理请求并返回所需的数据.然后 API 以 JSON 格式将数据返回给移动应用程序.这是当前的工作流程.
Eventually, in every API I'm sending the requests received from mobile app to the respective function present in a code base of my website. Then the respective function accepts the request and request parameters, process the request and returns the required data. Then the API returns the data to the mobile app in JSON format. This is the current work flow.
现在,我想让网站资源(即来自网站代码库和数据的功能)的可用性受到用户身份验证.简而言之,我想在这种情况下实现基于令牌的身份验证"方案.
Now, I want to make the availability of website resources (i.e. functions from website's code base and data) subject to user authentication. In short, I want to implement 'Token Based Authentication' scheme in this scenario.
以下应该是我实现基于令牌的身份验证"后的流程:
Following should be the flow after I implement 'Token Based Authentication':
当用户第一次通过向登录 API 发送请求中的用户名和密码登录系统时,在成功验证该特定用户的用户名和密码组合后将生成一个安全令牌.此安全令牌还将与用户名/密码/一些散列值一起存储到一些 MySQL 数据库表中,以在进一步处理中识别用户.如果验证失败,则不应生成安全令牌,用户也不应登录.
When user first time logs in to the system by sending user name and password in an request to the login API, one security token will get generate upon successful verification of the user name and password combination for that particular user. This security token will also get stored into some MySQL database table along with the user name/password/some hashed value to identify the user in further processing. If the verification fails the security token should not get generate and the user should also not log in.
登录成功后,生成的安全令牌将作为登录 API 的成功响应发送回用户.现在,直到用户使用每个后续请求登录,此令牌将被发送到相关 API,最终将被发送到身份验证函数以验证其有效性.
Upon successful login, the generated security token will be sent back to the user in success response of login API. Now until the user is logged in with each subsequent request this token will be sent to the concerned API and ultimately it will be sent to the authentication function for the authentication of it's validity.
如果有任何请求发送了无效令牌,则应发送请先登录"消息作为响应,并且网站资源不应获得访问权限.
If with any request an invalid token is sent then the "Please login first " message should be sent in response and the website resource should not get access.
一旦用户注销,此安全令牌条目应从数据库中删除或对其采取任何适当的操作.
Once user logs out this security token entry should be deleted from the database or whatever appropriate action should be taken on it.
自从我在职业生涯中第一次从事基于令牌的身份验证"以来,我的上述方法可能有误.如果我做错了什么,请纠正我的错误.
Since, I'm working on 'Token Based Authentication' for the first time in my career I might be wrong with my above approach. If I'm doing anything wrong please correct my mistake/s.
我找到了以下链接,但我发现这些链接没有多大用处,因为它们缺少带有分步描述的工作代码示例:
I found following links but those I didn't find much useful since they are lacking working code example with step-by-step description :
使用 Phalcon Micro 框架的 PHP HMAC Restful API
JWT 的 PEAR 包
如果你能提供完整的代码,包括创建数据库表、PHP 和 MySQL 之间的连接、然后创建安全令牌、检查当前登录用户的安全令牌的有效性等,那将是非常棒的对我来说.
If you could provide me the entire code which consists the database table creation, connectivity between PHP and MySQL, then creation of security token, checking the validity of security token for the current logged in user, etc. it would be really great for me.
另外,如果您可以为上述两个(或两个)选项中的任何一个提供工作代码示例作为您对这个问题的答案,那也将非常棒.如果除了我提供的两个完整的工作代码示例之外,您还有其他选择,欢迎您回答.
Also, if you could give the working code example for any of the above two (or both) options as your answer to this question that would also be very great. If you have any other option apart from the two I provided with complete working code example you are welcome with your answer.
注意:- 请不要建议我使用 OAuth 身份验证过程.
N.B.:- Please don't suggest me to use OAuth authentication process.
以下是我自己尝试的代码,但我不知道它是对还是错.我的方法是对还是错?
Following is the code I tried on my own but I don't know whether it's right or wrong. Is my approach correct or wrong?
为了创建一个令牌,我使用这个函数,它以用户数据为参数
To create a token I use this function which takes as parameters, the user's data
define('SECRET_KEY', "fakesecretkey");
function createToken($data)
{
/* Create a part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"]; // It can be 'stronger' of course
/* Encoding token */
$token = hash('sha256', $tokenGeneric.$data);
return array('token' => $token, 'userData' => $data);
}
因此,用户可以验证自己并接收包含令牌(genericPart + 他的数据,已编码)和未编码的 hisData 的数组:
So a user can authentified himself and receive an array which contains a token (genericPart + his data, encoded), and hisData not encoded :
function auth($login, $password)
{
// we check user. For instance, it's ok, and we get his ID and his role.
$userID = 1;
$userRole = "admin";
// Concatenating data with TIME
$data = time()."_".$userID."-".$userRole;
$token = createToken($data);
echo json_encode($token);
}
然后用户可以将他的令牌 + 他的未编码数据发送给我以便检查:
Then the user can send me his token + his un-encoded data in order to check :
define('VALIDITY_TIME', 3600);
function checkToken($receivedToken, $receivedData)
{
/* Recreate the generic part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"];
// We create a token which should match
$token = hash('sha256', $tokenGeneric.$receivedData);
// We check if token is ok !
if ($receivedToken != $token)
{
echo 'wrong Token !';
return false;
}
list($tokenDate, $userData) = explode("_", $receivedData);
// here we compare tokenDate with current time using VALIDITY_TIME to check if the token is expired
// if token expired we return false
// otherwise it's ok and we return a new token
return createToken(time()."#".$userData);
}
$check = checkToken($_GET['token'], $_GET['data']);
if ($check !== false)
echo json_encode(array("secureData" => "Oo")); // And we add the new token for the next request
我说的对吗?
推荐答案
根据我们的讨论,您可以做一些类似于 OAuth2.0 的事情.我建议实施完整的规范,但由于它是您的应用程序,您可以进行更改.
Based on our discussion, you could do something akin to OAuth2.0. I would recommend implementing the full spec, but since it is your application, you could make changes.
这是来自 RFC 6750
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| (Slim API) |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
在 slim 中,您最多可以有三个端点:
In slim, you could have as few as three endpoints:
POST 用户名/密码:
POST username/password:
/oauth/v1/authenticate/
返回 { token: foo }
GET 其中 {token} 是您的唯一令牌
GET where {token} is your unique token
/oauth/v1/token/{token}
返回 { 用户名:joe, permissions['page:admin','users:full'], expires: 123456}
DELETE 传递 {token}
DELETE pass the {token}
/oauth/v1/token/revoke
回复 200 OK
和一个空的正文.
replies with 200 OK
and an empty body.
现在,它是如何工作的:
Now, how it works:
- 当他们进行身份验证时,只需返回一个带有您的
token
的 JSON 对象,客户端将其存储在类似 cookie 的东西中. - 客户端将其在 RFC 中标识的标头中传递给您的资源服务器 第 2.1 节(在数据库/nosql/whatever 中查找用户权限):
- When they auth, just return a JSON object with your
token
, which the client stores in something like a cookie. - The client passes this to your resource server in a header as identified in the RFC section 2.1 (which looks up the user permissions in a database/nosql/whatever):
GET/resource HTTP/1.1
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
您的资源服务器与后端的 Slim API 联系以确定您的权限.然后服务器决定你可以看到什么.
Your resource server contacts the Slim API on the back-end to determine your permissions. The server then decides what you're allowed to see.
如果您不喜欢将其作为标头发送,请参阅 第 2.2 节,描述如何在正文中发送,或 第 2.3 节 将其作为 URI 查询发送.
If you don't like sending it as a header, see section 2.2, which describes how to send it in the body, or section 2.3 which sends it as a URI query.
这些显然不需要是不同的服务器.您可以按照自己的意愿实现它.
These obviously do not need to be different servers. You can implement it how you wish.
相关文章