使用令牌实现 RESTful API 身份验证 (Yii/Yii2)
Posted
技术标签:
【中文标题】使用令牌实现 RESTful API 身份验证 (Yii/Yii2)【英文标题】:Implementing an RESTful API Authentication using tokens (Yii/Yii2) 【发布时间】:2014-10-09 06:02:15 【问题描述】:我正在 Yii 1.x 中构建一个 API,它将与移动应用程序一起使用。该过程的一部分涉及使用以下 JSON 请求登录(使用用户名和密码):-
// 使用用户名和密码发送请求
"request" :
"model" :
"username" : "bobbysmith",
"password" : "mystrongpassword"
//如果登录成功返回如下响应
"response":
"code": 200,
"message": "OK",
"model":
"timestamp": 1408109484,
"token": "633uq4t0qdtd1mdllnv2h1vs32"
此令牌非常重要 - 一旦用户登录应用程序,我希望他们能够访问需要他们登录的其他页面。我希望移动应用程序存储此令牌并且如果相同633uq4t0qdtd1mdllnv2h1vs32 令牌在任何后续请求中发现,它将接受此作为经过身份验证的请求(对于此用户“bobbysmith”)。
我有点不确定如何最好地做到这一点,我已经做了一些研究,并且可以多次提到 oAuth,以及通过 HTTPS 进行的基本身份验证。
简而言之,这...
-
在移动应用主页上,用户使用其用户名和密码正确登录,这会向 API 发送请求。
这将返回带有当前时间戳和所有重要令牌的成功响应(如上所示)。
同一用户转到另一个应用页面/视图,其中 a) 需要此令牌,并且 b) 如果匹配,则验证该用户身份(例如,以便他们可以编辑该帐户等)
一旦用户单击“注销”,此令牌就会被删除(并且可以再访问我的帐户等) - 本质上是一个基于令牌的身份验证系统。
谁能解释实现这一目标的最佳方法?如果我所说的不是 100% 清楚,请告诉我,我会提供更多信息。
当我使用 php 时,Yii 1.x 解决方案是理想的,因为这是当前 API 所使用的构建。
简而言之,应用程序确保对服务器的每个请求都在有效负载或标头中包含令牌,因此可以在每个后续帖子中检索此令牌,一旦注销,此令牌将被简单地删除或设置为空/空
【问题讨论】:
【参考方案1】:有关处理安全接口的信息
专注于一次提供所有良好(RESTful)身份验证内容的解决方案,这可能是:
SSL(最重要的是,否则“HTTP-Auth”会少一些,每个人都可以读出您的请求标头/正文Man-in-the-middle-attack) oAuth(或更好的 oAuth2!) HTTP-Auth 令牌(包括有限的生命周期、刷新,并且可能包括 IP/DeviceUID 检查逻辑 -> 如果它是移动的!) Salt 生成的密码 自定义 HTTP 标头用于 ID/ENV/CLIENT 检查或其他任何情况。 请求和响应的加密正文数据,以防止数据操纵提示:个人用户数据应始终加密!
也许是一个解决方案
您可以在上面看到有关安全接口的标准信息。为了确保持久的安全性,您可以像在下一部分中一样尝试它。我不确定您的 AppSidePersitence。也许它的 sqlLite 或类似的东西。这就是为什么我没有指出基于代码的 DB-Schema,就像我对 Yii 所做的那样。您需要在 Yii 应用程序(后端)和应用程序(客户端)内部存储/持久化来存储时间和令牌。
你的 YiiDBModel
-- -----------------------------------------------------
-- Table `mydb`.`user`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`user` (
`id` INT NOT NULL,
`username` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`lastLogin` DATETIME NULL,
`modified` DATETIME NULL,
`created` DATETIME NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
----------------------------------------------------
-- Table `mydb`.`authToken`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`authToken` (
`id` INT NOT NULL,
`userId` INT NOT NULL,
`token` VARCHAR(255) NOT NULL,
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`, `userId`),
INDEX `fk_authToken_user_idx` (`userId` ASC),
UNIQUE INDEX `token_UNIQUE` (`token` ASC),
CONSTRAINT `fk_authToken_user`
FOREIGN KEY (`userId`)
REFERENCES `mydb`.`user` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
您的 AppPersitenceModel
正确处理您的令牌并确保登录安全
-
您在 Yii-Application 端生成的任何令牌都将与“created”-Datetime 一起存储在您的 YiiApp-Database 中(参见表 authToken)。每个用户只有一个“token”,它会在正确的“login”-Request 后生成。这样,您无需在“登录”时检查令牌。由模式定义,“令牌”是唯一的!我认为没有必要处理历史数据(过期的旧令牌)。
一旦用户登录被 Yii 验证为“成功”,您将生成一个具有当前时间戳的新用户令牌,该令牌将存储在您的 YiiApp-DB 中。在您的 YiiApp 中您需要配置一个“过期时间”,它将添加到当前时间戳中,例如,如果您喜欢使用“时间戳”: 当前时间戳为:1408109484
并且您的过期时间设置为 @987654327 @(即 3600 秒 = 1 小时)。所以...您将通过 API 发送的过期日期时间是(1408109484+3600)
。顺便提一句。提示:您不需要发送"code": 200
之类的属性。响应代码包含在您的请求/响应标头数据中。
** 200 OK Response-Example,用户登录成功后,持有计算的“过期”-date:**
"error": null,
"content":
"expires": 1408109484,
"token": "633uq4t0qdtd1mdllnv2h1vs32"
重要提示:您想要保护的每个请求都需要使用您生成的用户“令牌”发送。这可能会存储在您的 deviceStorage 中。您可以处理您的“登录状态”——如果您使用HTTP-Response-Codes 正确,则可以处理您的“登录状态”,例如,200 OK(如果一切正常)或 401(未授权,用户未登录或会话已过期)。您需要在 Yii 端验证您的用户请求。从传入请求中读取令牌,根据数据库中的给定令牌对其进行验证,并将“创建”-DB 与当前传入的请求时间(HTTP 请求)进行比较。
** 请求示例,任何安全请求的默认架构:**
"token": "633uq4t0qdtd1mdllnv2h1vs32"
"content":
"someDataYouNeed" : null
** 401 Unauthorized Response-Example, token expired :**
"error": 1, // errorCode 1: token is expired
"content":
"someDataYouNeed" : null
** 401 Unauthorized Response-示例,用户未登录(YiiDB 中不存在令牌):**
"error": 2, // errorCode 2: user is not logged in
"content":
"someDataYouNeed" : null
让用户会话保持活跃?这很容易。只需将authToken
-Table 中的“created”-Date 更新为当前请求时间。每次这样做,用户都会发送一个有效的请求。这样,如果用户仍处于活动状态,会话将不会过期。在更新 DB 中的 expires
-Date 字段之前,请确保您的 DB-Token 未过期。如果会话到期时没有发送请求,则无法再保持活动状态。
抱歉,添加 PHP 代码太多了。
【讨论】:
感谢您的回复 - 目前我正在保存 Yii sessionID 并将其保存为响应中的令牌。是否可以传递此令牌(来自 Yii 的 sessionID)并将其用作后续请求的身份验证,如果可以,任何人都可以提供有关我如何做到这一点的任何信息? @Zabs,我添加了一些代码,信息和几乎一个过程解释。 几个月后回到这个...仍然是一个很棒的回应我喜欢 ***。 :) lin - 您可能对 JWT 拥有专利权 :)【参考方案2】:如果您正在构建原生移动应用程序,那么明智的做法是依赖原生内存(例如 ios 钥匙串)的安全性,而不是基于 cookie 的解决方案。否则你所描述的似乎很好。只要您的有效负载是通过 SSL 发送的,令牌是在 PUT 还是在 POST 中并不重要。您的令牌管理(即到期时间)是您必须做出的业务决策。后端我会按照您的描述将令牌保存在您的数据库中,并在它因任何原因失效时将其删除,并向您的客户端应用程序返回一条消息,以将其放回注销模式/重新请求凭据。
编辑:从多产的 Phil Sturgeon 那里看看这个很棒的 tut。他还有一个很棒的 CI 库,用于在 CI 中构建 RESTful API,这可能值得一看。
http://philsturgeon.uk/blog/2013/07/building-a-decent-api
http://code.tutsplus.com/tutorials/working-with-restful-services-in-codeigniter--net-8814
【讨论】:
【参考方案3】:此时您可能已切换到 Yii2,为了将来参考,最简洁的解决方案是使用 RESTful API 的包含类,或者可以在任何框架中实现它们。
来源:HttpBearerAuth.php
优点在this article 中已充分说明,但总而言之,最好使用带有请求标头的解决方案,因为 GET 参数可能会保存在日志中,并且基本身份验证密码很容易被拦截如果你不使用 SSL(你应该!)
【讨论】:
以上是关于使用令牌实现 RESTful API 身份验证 (Yii/Yii2)的主要内容,如果未能解决你的问题,请参考以下文章
外部身份验证提供程序和对 RESTful API 的请求的身份验证
RESTful API 的令牌认证:是不是应该定期更改令牌?