gpt4 book ai didi

symfony - 如何实现 FosOAuthServerBundle 以保护 REST API?

转载 作者:行者123 更新时间:2023-12-03 07:49:49 24 4
gpt4 key购买 nike

我想提供一个使用 FOSOAuthServerBundle 的 OAuth2 保护的 RESTful API,我不太确定我必须做什么。

我遵循了基本步骤 from the documentation但是有些东西丢失了,我找不到我需要的完整示例。

所以,我尽我所能去理解 this example of implementation (我找到的唯一一个)但仍有一些我不明白的地方。

首先,为什么我们需要 API 中的登录页面?假设我的客户端是 iPhone 或 Android 应用程序,我在应用程序上看到登录页面的兴趣,但我认为客户端只需从 API 调用网络服务即可获取其 token ,我错了吗?那么如何通过 REST 端点实现自动化和 token 提供呢?

然后,文档告诉编写此防火墙:

oauth_authorize:
pattern: ^/oauth/v2/auth
# Add your favorite authentication process here

而且我不知道如何添加身份验证过程。我应该自己写一个吗,例如以下 this tutorial还是我完全错了?

在全局范围内,有人可以花时间解释在文档中的五个步骤之后提供 OAuth2 安全 RESTful API 所需的过程吗?会非常好...

在@Sehael 回答之后编辑:

在它完美之前我还有一些问题......

这里的“客户端”代表什么?例如,我应该为 iPhone 应用程序创建一个客户端,为 Android 应用程序创建另一个客户端吗?我是否必须为每个想要使用 API 的实例创建一个新客户端?在这种情况下,最佳做法是什么?

与您不同,我不使用前端网站的 OAuth 流程,而是使用“经典”的 symfony 方式。你觉得这很奇怪,还是正常的?

refresh_token 有什么用处?如何使用它?

我试图测试我的新 OAuth 保护服务。我使用了支持 OAuth 1.0 的 POSTman chrome 扩展,OAuth2 看起来像 OAuth1 是否足以用 POSTman 进行测试?有一个“ secret token ”字段,我不知道如何填写。如果我不能,我很高兴看到你提出的 (@Sehael) PHP 类(class)。/编辑:好的,我想我找到了这个答案。我刚刚添加了 access_token作为以 token 为值的 GET 参数。它似乎正在工作。不幸的是,我必须对捆绑代码进行反向生成才能找到它,而不是在文档中阅读它。

无论如何,非常感谢!

最佳答案

我还发现文档可能有点困惑。但是经过几个小时的尝试,我在 this blog 的帮助下弄明白了(更新 - 博客不再存在,改为 Internet Archive)。在您的情况下,您不需要 ^/oauth/v2/auth 的防火墙条目。因为这是用于授权页面。您需要记住 oAuth 能够做什么……它不仅仅用于 REST api。但是,如果 REST api 是您想要保护的东西,那么您就不需要它。这是我的应用程序中的示例防火墙配置:

firewalls:

oauth_token:
pattern: ^/oauth/v2/token
security: false

api_firewall:
pattern: ^/api/.*
fos_oauth: true
stateless: true
anonymous: false

secure_area:
pattern: ^/
fos_oauth: true
form_login:
provider: user_provider
check_path: /oauth/v2/auth_login_check
login_path: /oauth/v2/auth_login
logout:
path: /logout
target: /
anonymous: ~

access_control:
- { path: ^/oauth/v2/auth_login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }

请注意,您需要定义一个用户提供程序。如果您使用 FOSUserBundle,则已经为您创建了一个用户提供程序。就我而言,我自己制作并从中创建了一项服务。

在我的 config.yml 中:
fos_oauth_server:
db_driver: orm
client_class: BB\AuthBundle\Entity\Client
access_token_class: BB\AuthBundle\Entity\AccessToken
refresh_token_class: BB\AuthBundle\Entity\RefreshToken
auth_code_class: BB\AuthBundle\Entity\AuthCode
service:
user_provider: platform.user.provider
options:
supported_scopes: user

我还应该提到,您在数据库中创建的表(access_token、client、auth_code、refresh_token)需要具有比文档中显示的更多的字段......

访问 token 表: id(int)、client_id(int)、user_id(int)、token(string)、scope(string)、expires_at(int)

客户端表: id(int)、random_id(string)、secret(string)、redirect_urls(string)、allowed_grant_types(string)

授权码表: id(int)、client_id(int)、user_id(int)

刷新 token 表: id(int), client_id(int), user_id(int), token(string), expires_at(int), scope(string)

这些表将存储 oAuth 所需的信息,因此请更新您的 Doctrine 实体,使其与上述 db 表相匹配。

然后你需要一种方法来实际生成 secret 和 client_id,所以这就是文档的“创建客户端”部分的用武之地,尽管它不是很有帮助......

/src/My/AuthBundle/Command/CreateClientCommand.php 创建文件(您将需要创建文件夹 Command )此代码来自我上面链接的文章,并显示了您可以放入此文件的内容的示例:
<?php
# src/Acme/DemoBundle/Command/CreateClientCommand.php
namespace Acme\DemoBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CreateClientCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('acme:oauth-server:client:create')
->setDescription('Creates a new client')
->addOption(
'redirect-uri',
null,
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets redirect uri for client. Use this option multiple times to set multiple redirect URIs.',
null
)
->addOption(
'grant-type',
null,
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets allowed grant type for client. Use this option multiple times to set multiple grant types..',
null
)
->setHelp(
<<<EOT
The <info>%command.name%</info>command creates a new client.

<info>php %command.full_name% [--redirect-uri=...] [--grant-type=...] name</info>

EOT
);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$clientManager = $this->getContainer()->get('fos_oauth_server.client_manager.default');
$client = $clientManager->createClient();
$client->setRedirectUris($input->getOption('redirect-uri'));
$client->setAllowedGrantTypes($input->getOption('grant-type'));
$clientManager->updateClient($client);
$output->writeln(
sprintf(
'Added a new client with public id <info>%s</info>, secret <info>%s</info>',
$client->getPublicId(),
$client->getSecret()
)
);
}
}

然后要实际创建 client_id 和 secret,从命令行执行此命令(这会将必要的 id 和内容插入到数据库中):
php app/console acme:oauth-server:client:create --redirect-uri="http://clinet.local/" --grant-type="password" --grant-type="refresh_token" --grant-type="client_credentials"
请注意 acme:oauth-server:client:create可以是您在 CreateClientCommand.php 中实际命名的任何命令文件与 $this->setName('acme:oauth-server:client:create') .

获得 client_id 和 secret 后,您就可以进行身份​​验证了。在浏览器中发出如下请求:
http://example.com/oauth/v2/token?client_id=[CLIENT_ID_YOU GENERATED]&client_secret=[SECRET_YOU_GENERATED]&grant_type=password&username=[USERNAME]&password=[PASSWORD]
希望它对你有用。肯定有很多配置,只需尝试一步一步。

我还编写了一个简单的 PHP 类来使用 oAuth 调用我的 Symfony REST api,如果您认为这有用,请告诉我,我可以传递它。

更新

针对您的进一步问题:

“客户端”在同一个博客上有描述,只是一篇不同的文章。阅读 Clients and Scopes在这里,它应该向您阐明什么是客户。就像文章中提到的那样,您不需要为每个用户都配备一个客户端。如果需要,您可以为所有用户使用一个客户端。

我实际上也在我的前端站点使用经典的 Symfony 身份验证,但将来可能会改变。因此,将这些事情牢记在心总是好的,但我不会说将这两种方法结合起来很奇怪。

当 access_token 已过期并且您希望请求新的 access_token 而不重新发送用户凭据时,将使用 refresh_token。相反,您发送刷新 token 并获得新的 access_token。这对于 REST API 来说并不是真正必要的,因为单个请求可能不会花费足够长的时间来使 access_token 过期。

oAuth1 和 oAuth2 非常不同,所以我假设您使用的方法不起作用,但我从未尝试过。但是只是为了测试,你可以做一个普通的GET或POST请求,只要你通过 access_token=[ACCESS_TOKEN]在 GET 查询字符串中(实际上适用于所有类型的请求)。

但无论如何,这是我的课。我使用了一个配置文件来存储一些变量,我没有实现 DELETE 的能力,但这并不太难。
class RestRequest{
private $token_url;
private $access_token;
private $refresh_token;
private $client_id;
private $client_secret;

public function __construct(){
include 'config.php';
$this->client_id = $conf['client_id'];
$this->client_secret = $conf['client_secret'];
$this->token_url = $conf['token_url'];

$params = array(
'client_id'=>$this->client_id,
'client_secret'=>$this->client_secret,
'username'=>$conf['rest_user'],
'password'=>$conf['rest_pass'],
'grant_type'=>'password'
);

$result = $this->call($this->token_url, 'GET', $params);
$this->access_token = $result->access_token;
$this->refresh_token = $result->refresh_token;
}

public function getToken(){
return $this->access_token;
}

public function refreshToken(){
$params = array(
'client_id'=>$this->client_id,
'client_secret'=>$this->client_secret,
'refresh_token'=>$this->refresh_token,
'grant_type'=>'refresh_token'
);

$result = $this->call($this->token_url, "GET", $params);

$this->access_token = $result->access_token;
$this->refresh_token = $result->refresh_token;

return $this->access_token;
}

public function call($url, $method, $getParams = array(), $postParams = array()){
ob_start();
$curl_request = curl_init();

curl_setopt($curl_request, CURLOPT_HEADER, 0); // don't include the header info in the output
curl_setopt($curl_request, CURLOPT_RETURNTRANSFER, 1); // don't display the output on the screen
$url = $url."?".http_build_query($getParams);
switch(strtoupper($method)){
case "POST": // Set the request options for POST requests (create)
curl_setopt($curl_request, CURLOPT_URL, $url); // request URL
curl_setopt($curl_request, CURLOPT_POST, 1); // set request type to POST
curl_setopt($curl_request, CURLOPT_POSTFIELDS, http_build_query($postParams)); // set request params
break;
case "GET": // Set the request options for GET requests (read)
curl_setopt($curl_request, CURLOPT_URL, $url); // request URL and params
break;
case "PUT": // Set the request options for PUT requests (update)
curl_setopt($curl_request, CURLOPT_URL, $url); // request URL
curl_setopt($curl_request, CURLOPT_CUSTOMREQUEST, "PUT"); // set request type
curl_setopt($curl_request, CURLOPT_POSTFIELDS, http_build_query($postParams)); // set request params
break;
case "DELETE":

break;
default:
curl_setopt($curl_request, CURLOPT_URL, $url);
break;
}

$result = curl_exec($curl_request); // execute the request
if($result === false){
$result = curl_error($curl_request);
}
curl_close($curl_request);
ob_end_flush();

return json_decode($result);
}
}

然后要使用该类,只需:
$request = new RestRequest();
$insertUrl = "http://example.com/api/users";
$postParams = array(
"username"=>"test",
"is_active"=>'false',
"other"=>"3g12g53g5gg4g246542g542g4"
);
$getParams = array("access_token"=>$request->getToken());
$response = $request->call($insertUrl, "POST", $getParams, $postParams);

关于symfony - 如何实现 FosOAuthServerBundle 以保护 REST API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21259190/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com