【问题标题】:Use a Semaphore to fix a Concurrency Issue使用信号量修复并发问题
【发布时间】:2020-10-28 01:32:58
【问题描述】:

我最近问了一个问题:链接 here,关于在我的 autodesk-forge Web 应用程序中存储刷新令牌的最佳方式。我目前将刷新令牌存储在一个只有一行和一列的 SQL 数据库中,其中包含刷新令牌。关于token的步骤如下:

  1. 当用户登录时,会调用 GET 方法从数据库中检索最新的令牌。 Returndata.php 只是连接到 SQL DB 并从表中检索行。获取方法代码:
function getRefreshToken() {
    $.get("returndata.php",
        function(response) {
            var res = JSON.parse(response);
           console.log(response);
            console.log(res);
            refreshToken = res[0].Value;
           // console.log(refreshToken);
            useRefresh();
           // console.log(response.value);
            //var times = response.times;
        }, 
    );  
}
  1. 返回令牌并用于获取用户的访问令牌。
  2. 获取访问令牌时,会附带一个刷新令牌,随后将其保存在 SQL 数据库中与之前的刷新令牌相同的行中。 Savesettings.php 只是连接到数据库并使用新的刷新令牌更新单行。 POST 方法存储刷新令牌的代码:
function saveRefreshToken() {
    
    $.post("savesettings.php",
    {
       Value: refreshToken,
    },
    function(data, status){
    console.log(data);
  });
    
}

在大多数情况下,此方法运行良好,但也有几次出现问题。我相信这是由于并发问题。如果两个用户在非常相似的时间访问数据库,则数据可能会损坏或提供错误的数据。为了解决这个问题,我知道我需要使用信号量,以便其他请求等到第一个请求完成后再执行。如何使用信号量来实现这一目标?任何帮助将不胜感激,因为我非常坚持这个问题。谢谢。干杯!!

编辑

function getToken(){
    $.ajax({
    method: 'POST',
    url: 'https://developer.api.autodesk.com/authentication/v1/gettoken',
    headers: {
        'Content-Type':'application/x-www-form-urlencoded'
    },
    data:'client_id=xxxxxxxxxxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxx&grant_type=authorization_code&code=' + code + '&redirect_uri=xxxxxxxxxxxxxxxxxxxxxx',
    
    success:function(response){
       // console.log(response);
        access_token = response.access_token;
        console.log(access_token);
        console.log(response);
        refreshToken = response.refresh_token;
        saveRefreshToken()
        
    }
})
}

function useRefresh(){
$.ajax({
    method:'POST',
    url: 'https://developer.api.autodesk.com/authentication/v1/refreshtoken',
    headers: {
        'Content-Type':'application/x-www-form-urlencoded'
    },
    data:'client_id=xxxxxxxxxxxxxxxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxxxx&grant_type=refresh_token&refresh_token='+refreshToken+'&scope=data:read',
success:function(response){
    console.log(response);
    refreshToken = response.refresh_token;
    //console.log(refreshToken);
    access_token = response.access_token;
    saveRefreshToken();
}
})
}

【问题讨论】:

  • 看到上面的代码意味着你在浏览器中暴露了你的令牌,对吧?这是一个安全漏洞,你不应该这样做。风险在于有人窃取了您的令牌,如果原始范围太高,您正在损害您的数据。您可能需要重构您的应用程序以保护令牌。我很乐意在这方面为您提供帮助。
  • 再次感谢您的帮助。我目前正在这样做,但是当用户使用刷新令牌获取令牌时,它的唯一范围是数据:读取。但是,当我授权时,我也有一个 data:write。如果用户可以获得 data:write 令牌,那将是安全漏洞。那么,我应该如何处理令牌而不是@cyrille
  • 只是添加 @cyrille ,我无法下载 NPM 包,所以我无法使用 express.js 之类的东西
  • 我正在从 LAN 运行我的网络应用程序,当我尝试安装 NPM 包时,我收到以下消息: \\team\wwwroot' CMD.EXE 以上述路径作为当前路径启动目录。不支持 UNC 路径。默认为 Windows 目录。 @cyrille 我现在已经在我的帖子中包含了我的令牌函数
  • 您可以通过 cyrille @ autodesk 与我联系。 com

标签: php sql semaphore autodesk-forge


【解决方案1】:

Evert 仅与 MySQL 竞速问题有关时是正确的。在您的情况下,您还调用另一台服务器来获取访问令牌;这意味着我们需要在刷新时锁定表以进行读/写。

我写了一个小例子here,它做了最少的事情来解决这个问题。但是,正如 Evert 所说,如果您选择信号量方法,它只能在单个服务器上处理 HTTP 请求。如果你在做负载均衡,我们需要使用 mysql/redis 锁的方法。我说 Redis,因为在这个话题上它会比 mysql 更好。

该示例还将向您展示在 Forge 应用程序中获取令牌的正确方法。在上面的代码中,我注意到您使用的是 $.ajax() 代码,这意味着您正在从 HTML 页面(在客户端上?)在 OAuth 服务器上进行 HTTP 调用 - 这样做会使您面临高安全性问题,您正在泄漏您的访问令牌和客户端 ID/密钥。任何人都可以在您不知情的情况下窃取您的客户 ID/秘密并开始使用您的帐户。

您应该发出 2 个访问令牌,一个具有最低权限的公众(这个仅用于查看器)。第二个令牌具有额外的权限,将保留在服务器上,永远不会暴露给任何客户端应用程序。

这个例子做了两个简单的操作:

  • 使用公共令牌在查看器中显示模型
  • 使用内部令牌列出存储桶或文件夹中的模型

该示例还显示了 2 条腿的流程和 3 条腿的代码流。虽然理论上两者应该以相同的方式工作,但有一个重要的区别需要知道。

  1. 您可以随时生成任意数量的 2 腿代币。这意味着您将始终获得一个有效的令牌,即使您在同一时刻多次使用您的令牌。实际上,您不要刷新令牌,每次都创建一个新令牌,这样做不会使先前生成的令牌无效。该示例有点懒惰,只需在需要时立即获取令牌,而无需打扰服务器处理的 HTTP 请求。

  2. 虽然您也可以生成任意数量的 3 腿令牌(代码流),但您会受到每次创建新令牌时都需要登录这一事实的限制。但是,当您创建访问令牌时,您会获得一个刷新令牌,它可以让您在每次需要时更新 access_token。但是,刷新令牌有 3 个注意事项:

    • 刷新令牌的有效期只有 14 天,因此您需要至少每 14 天刷新一次令牌。
    • 当您第一次创建令牌时,您需要提供您可能需要的所有范围。刷新令牌时,您可以降级范围,但永远无法添加新范围。
    • 每当您使用刷新令牌时,这个令牌都会丢失,但您会得到一个新令牌。最后一个令牌是您接下来需要使用的令牌。

要使用 2legged 版本,只需进入服务器的根目录,例如 http://localhost/

要使用 3legged 版本,请转到 http://localhost/login 以创建一对新令牌,否则请转到 http://localhost/www/bim360.html

还有一个小测试示例会在您的服务器上同时触发 5 个 HTTP 请求。我将我的 Apache 实例限制为一次处理 2 个请求,这是为了验证在更新令牌时,所有请求都不会相互竞争,这将使您的令牌/刷新令牌不同步。

readme 应该有足够的详细信息来部署示例和浏览代码。

【讨论】:

  • 一切看起来都很好,但是当我尝试运行时出现以下错误:
  • ( ! ) 致命错误:未捕获的 Dotenv\Exception\InvalidPathException:无法读取 [C:\xampp\htdocs\simple-forge-php-sample\server 处的任何环境文件\.env]。在 C:\xampp\htdocs\simple-forge-php-sample\vendor\vlucas\phpdotenv\src\Store\FileStore.php 第 57 行(!)Dotenv\Exception\InvalidPathException:无法读取任何环境文件( s) 在 [C:\xampp\htdocs\simple-forge-php-sample\server\.env]。在 C:\xampp\htdocs\simple-forge-php-sample\vendor\vlucas\phpdotenv\src\Store\FileStore.php 第 57 行 @cyrille
  • 我们见面,我将帮助您部署示例。对于上述错误,您可能忘记在服务器文件夹中创建 .env 文件。
  • 当我将 .env 文件放在服务器文件夹中时,我收到错误,即找不到带有网址的网页:localhost/simple-forge-php-sample/index.php@cyrille
  • 我也找不到 js 和 css 文件夹中的文件,我收到错误:bim360.html:16 GET localhost/www/www/www/js/bim360.js net::ERR_ABORTED 404 (Not Found) bim360.html:15 GET localhost/www/www/www/css/index.css net::ERR_ABORTED 404(未找到)bim360.html:16 GET localhost/www/www/www/js/bim360.js net::ERR_ABORTED 404(未找到)bim360.html:15 GET localhost/www/www/www/css/index.css net::ERR_ABORTED 404(未找到)@西里尔
【解决方案2】:

如果您遇到 PHP 和 MySQL 的并发问题,信号量不是解决此问题的正确级别。例如,如果您想运行第二个服务器来处理 PHP 请求怎么办。

处理此问题的正确方法是在您的数据库中。如果在单个请求期间发生损坏,则可能是您的查询错误或者您需要使用事务或锁。

【讨论】:

    猜你喜欢
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 2013-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多