使用令牌实现 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 的请求的身份验证

Openid 和 RestFul API

RESTful API 的令牌认证:是不是应该定期更改令牌?

在 RESTful Web API 中验证后续请求的令牌签名

如何存储 RESTful API 的身份验证数据

是否根据身份验证凭据 RESTful 返回不同的输出?