如何实现FosOAuthServerBundle来保护REST API?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何实现FosOAuthServerBundle来保护REST API?相关的知识,希望对你有一定的参考价值。
我想使用FOSOAuthServerBundle提供一个使用OAuth2保护的RESTful API,我不确定我该做什么。
我遵循基本步骤from the documentation,但有些事情缺失,我找不到我需要的完整例子。
所以,我试图了解最好的this example of implementation(我发现的唯一一个),但仍然有一些我不明白的事情。
首先,为什么我们需要API中的登录页面?假设我的客户端是iPhone或android应用程序,我看到应用程序上登录页面的兴趣,但我认为客户端只需从API调用Web服务来获取其令牌,我错了吗?那么如何通过REST端点实现自动化和令牌提供?
然后,文档告诉写这个防火墙:
oauth_authorize:
pattern: ^/oauth/v2/auth
# Add your favorite authentication process here
我不知道如何添加身份验证过程。我应该写自己的一个,例如跟随this tutorial还是我完全错了?
在全球范围内,有人可以花时间在文档中的五个步骤之后解释所需的过程,以提供OAuth2安全的RESTful API吗?这将是非常好的......
在@Sehael回答之后编辑:
在它完美之前我还有一些问题......
这里的“客户”代表什么?对于exemaple,我应该为iPhone应用程序创建一个客户端,为Android应用程序创建另一个客户端吗?我是否必须为每个想要使用API的实例创建一个新客户端?在这种情况下,最佳做法是什么?
与您不同,我不会将OAuth流程用于前端网站,而是使用“经典”symfony方式。你觉得这很奇怪,还是正常的?
refresh_token的用处是什么?如何使用它?
我试图测试我的新OAuth受保护服务。我使用支持OAuth 1.0的POSTman chrome扩展,OAuth2看起来像OAuth1足以用POSTman进行测试吗?有一个“秘密令牌”字段,我不知道如何填写。如果我不能,我很高兴看到你的(@Sehael)php课程,就像你提出的那样。 /编辑:好的我觉得我找到了这个答案。我刚刚将access_token
添加为GET参数,并将令牌作为值。它似乎工作。不幸的是,我必须对bundle代码进行反向engenering才能找到它而不是在文档中读取它。
无论如何,非常感谢!
我还发现文档可能有点令人困惑。但经过几个小时的尝试,我在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: BBAuthBundleEntityClient
access_token_class: BBAuthBundleEntityAccessToken
refresh_token_class: BBAuthBundleEntityRefreshToken
auth_code_class: BBAuthBundleEntityAuthCode
service:
user_provider: platform.user.provider
options:
supported_scopes: user
我还要提一下,您在数据库中创建的表(access_token,client,auth_code,refresh_token)需要的字段多于文档中显示的字段...
访问令牌表:id(int),client_id(int),user_id(int),token(字符串),范围(字符串),expires_at(int)
客户端表:id(int),random_id(字符串),secret(字符串),redirect_urls(字符串),allowed_grant_types(字符串)
验证代码表:id(int),client_id(int),user_id(int)
刷新令牌表:id(int),client_id(int),user_id(int),token(字符串),expires_at(int),范围(字符串)
这些表将存储oAuth所需的信息,因此更新您的Doctrine实体,使它们与上面的db表匹配。
然后你需要一种方法来实际生成秘密和client_id,所以这就是文档的“创建客户端”部分,尽管它不是很有帮助...
在/src/My/AuthBundle/Command/CreateClientCommand.php
创建一个文件(你需要创建文件夹Command
)这段代码来自我上面链接的文章,并显示了你可以放入这个文件的例子:
<?php
# src/Acme/DemoBundle/Command/CreateClientCommand.php
namespace AcmeDemoBundleCommand;
use SymfonyBundleFrameworkBundleCommandContainerAwareCommand;
use SymfonyComponentConsoleInputInputArgument;
use SymfonyComponentConsoleInputInputOption;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
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,如果你认为这样有用,请告诉我,我可以传递它。
UPDATE
回答您的进一步问题:
“客户”在同一博客上描述,只是另一篇文章。在这里阅读Clients and Scopes部分,它应该为您澄清客户是什么。就像文章中提到的那样,每个用户都不需要客户端。如果需要,您可以为所有用户设置一个客户端。
我实际上也在为我的前端站点使用经典的Symfony身份验证,但这可能在将来发生变化。因此,将这些事情放在脑海中总是好的,但我不会说将这两种方法结合起来很奇怪。
当access_token已过期且您想要请求新的access_token而不重新发送用户凭据时,将使用refresh_token。相反,您发送刷新令牌并获得新的access_token。这对于REST API来说并不是必需的,因为单个请求可能不会花费足够长的时间来使access_token过期。
oAuth1和oAuth2是非常不同的,所以我认为你使用的方法不起作用,但我从来没有尝试过。但只是为了测试,只要您在GET查询字符串中传递access_token=[ACCESS_TOKEN]
(实际上是针对所有类型的请求),您就可以发出正常的GET或POST请求。
但无论如何,这是我的班级。我使用配置文件来存储一些变量,而我没有实现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(strtou以上是关于如何实现FosOAuthServerBundle来保护REST API?的主要内容,如果未能解决你的问题,请参考以下文章