29/12/2015 - SYMFONY
Bu örnek bize symfony ile client_credentials
grant type kullanan bir OAuth2 API'nin nasıl yaratılacağını gösterecek. Anlaşılması için adımları tek tek gideceğim. Eğer API @Security("has_role('xxxxx')")
annotation ile controllerleri control etmek için role_hierarchy
kullanmıyor ise, client_credentials
grant type en uygun ve basit seçenektir. Daha fazla bilgi için, yazının en altındaki referans linklerini de ziyaret edebilirsiniz. API'yi koruma altına almak için FOSOAuthServerBundle kullanacağız.
Aşağıdaki örnekte, access token'i elde etmek için kullandığım isteğin URI kısmında client_id
ve client_secret
bilgilerini sadece göstermek amacıyla açıkça teşhir ettim. Gerçek hayattaki uygulamalarınızda bunu yapmamalısınız. Onun yerine, her ikisini de base64_encode
fonksiyonu ile kodlayıp, isteğin kafasına Authorization
olarak ekleyin.
$clientId = 'i-am-client-id';
$clientSecret = 'i-am-client-secret';
$base64 = base64_encode($clientId.':'.$clientSecret);
$header = 'Basic '.$base64;
Sonuç olarak isteğinizde Authorization: Basic aS1hbS1jbGllbnQtaWQ6aS1hbS1jbGllbnQtc2VjcmV0
şeklinde kullanmanız gerekir. Bununla birlikte, URI kısmından grant_type
girdilerinide kaldırıp, application/x-www-form-urlencoded
ile kodlanmak üzere, istek parametresi olarak kullanmanız gerekir. Final isteğiniz aşağıdaki gibi olmalı.
curl -X POST
-H 'Authorization: Basic aS1hbS1jbGllbnQtaWQ6aS1hbS1jbGllbnQtc2VjcmV0'
-H 'content-type: application/x-www-form-urlencoded'
-d 'grant_type=client_credentials'
http://oauth-server.dev/app_dev.php/oauth/v2/token
refresh_token
içermez.access_token
varsayılan olarak "user" bilgisine sahip değildir bu nedenle, API/server uygulamasında "user" bilgisine ulaşmak mümkün olmaz. Sadece Authorization Code, Implicit Grant ve Password access_token
varsayılan olarak "user" bilgisine sahiplerdir.# /etc/apache2/sites-available/oauth-server.dev.conf
<VirtualHost *:80>
ServerName oauth-server.dev
DocumentRoot "/var/www/html/local/oauth-server/web"
<Directory "/var/www/html/local/oauth-server/web">
Options Indexes FollowSymlinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/oauth-server.dev.error.log
CustomLog ${APACHE_LOG_DIR}/oauth-server.dev.access.log combined
</VirtualHost>
# etc/hosts
127.0.0.1 oauth-server.dev
$ sudo service apache2 restart
Sisteminizde http://oauth-server.dev/
virtual host yaratın.
composer create-project symfony/framework-standard-edition oauth-server "2.6.9"
app/console generate:bundle --namespace=Application/ServerBundle
Prefix /1/
API'nin URL versiyonunu temsil eder.
# oauth-server/app/config/routing.yml
application_server:
resource: "@ApplicationServerBundle/Controller"
prefix: /1/
type: annotation
namespace Application\ServerBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* @Route("server", service="application_server.controller.server")
*/
class ServerController extends Controller
{
/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/team")
*
* @return Response
*/
public function getTeamAction(Request $request)
{
return new Response('GET your team from Server');
}
/**
* @param Request $request
*
* @Method({"POST"})
* @Route("/team")
*
* @return Response
*/
public function createTeamAction(Request $request)
{
return new Response('POST your team to Server');
}
}
services:
application_server.controller.server:
class: Application\ServerBundle\Controller\ServerController
# Request
GET http://oauth-server.dev/app_dev.php/1/server/team
# Response
HTTP/1.1 200 OK - GET your team from Server
# Request
POST http://oauth-server.dev/app_dev.php/1/server/team
{
"hello": "world"
}
# Response
HTTP/1.1 200 OK - POST your team to Server
Composer ile "friendsofsymfony/oauth-server-bundle": "1.4.2"
paketini yükle ve new FOS\OAuthServerBundle\FOSOAuthServerBundle(),
ile AppKernel.php içinde aktifleştir.
# oauth-server/app/config/config.yml
fos_oauth_server:
db_driver: orm
client_class: Application\ServerBundle\Entity\Client
access_token_class: Application\ServerBundle\Entity\AccessToken
refresh_token_class: FOS\OAuthServerBundle\Entity\RefreshToken # Not implemented in this example
auth_code_class: FOS\OAuthServerBundle\Entity\AuthCode # Not implemented in this example
service:
options:
access_token_lifetime: 3600
# oauth-server/app/config/routing.yml
fos_oauth_server_token:
resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"
# oauth-server/app/config/security.yml
security:
providers:
in_memory:
memory: ~
firewalls:
oauth_token:
pattern: ^/oauth/v2/token
security: false
secured_area:
pattern: ^/
fos_oauth: true
stateless: true
access_control:
- { path: ^/, roles: [ IS_AUTHENTICATED_FULLY ] }
# oauth-server/src/Application/ServerBundle/Entity/Client.php
namespace Application\ServerBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\OAuthServerBundle\Entity\Client as BaseClient;
/**
* @ORM\Entity
*/
class Client extends BaseClient
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
# oauth-server/src/Application/ServerBundle/Entity/AccessToken.php
namespace Application\ServerBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\OAuthServerBundle\Entity\AccessToken as BaseAccessToken;
/**
* @ORM\Entity
*/
class AccessToken extends BaseAccessToken
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
protected $client;
}
app/console doctrine:database:create
app/console doctrine:schema:update --force
Aşağıda gördüğümüz gibi API korunmuş durumda.
# Request
GET http://oauth-server.dev/app_dev.php/1/server/team
# Response
HTTP/1.1 401 Unauthorized
{
"error": "access_denied",
"error_description": "OAuth2 authentication required"
}
# Request
POST http://oauth-server.dev/app_dev.php/1/server/team
{
"hello": "world"
}
# Response
HTTP/1.1 401 Unauthorized
{
"error": "access_denied",
"error_description": "OAuth2 authentication required"
}
API'yi kullanmak isteyen her clientın client_id
ve client_secret
kodları olmalı. Bunlar API'yi kullanmak için gerekli olan access_token
yaratmada kullanılırlar. Aşağıdaki komut client_id
ve client_secret
kodlarını yaratmak için kullanılır.
# oauth-server/src/Application/ServerBundle/Command/CreateOauthClientCommand.php
namespace Application\ServerBundle\Command;
use FOS\OAuthServerBundle\Entity\Client;
use FOS\OAuthServerBundle\Entity\ClientManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class CreateOauthClientCommand extends Command
{
const REDIRECT_URI = 'redirect-uri';
const GRANT_TYPE = 'grant-type';
private $clientManager;
public function __construct(ClientManager $clientManager)
{
parent::__construct();
$this->clientManager = $clientManager;
}
protected function configure()
{
$this
->setName('create:oauth:client')
->setDescription('Creates a new OAuth client.')
->addOption(
self::REDIRECT_URI,
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Sets redirect uri for client. Can be used multiple times.'
)
->addOption(
self::GRANT_TYPE,
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Sets allowed grant type for client. Can be used multiple times.'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
/** @var Client $client */
$client = $this->clientManager->createClient();
$client->setRedirectUris($input->getOption(self::REDIRECT_URI));
$client->setAllowedGrantTypes($input->getOption(self::GRANT_TYPE));
$this->clientManager->updateClient($client);
$this->echoCredentials($output, $client);
}
private function echoCredentials(OutputInterface $output, Client $client)
{
$output->writeln('OAuth client has been created...');
$output->writeln(sprintf('Public ID:%s ', $client->getPublicId()));
$output->writeln(sprintf('Secret ID:%s ', $client->getSecret()));
}
}
services:
application_server.command.create_oauth_client:
class: Application\ServerBundle\Command\CreateOauthClientCommand
arguments:
- @fos_oauth_server.client_manager.default
tags:
- { name: console.command }
Aşağıdaki komutu çalıştırın ve kimlik bilgilerini paylaşmayın! Bu veritabanındaki client
tablosuna bir kayıt girecek.
Mac:oauth-server inanzzz$ app/console create:oauth:client --grant-type="client_credentials"
OAuth client has been created...
Public ID: 8_4mcij3t948isk880w0gskkksc88w0wo0wowogkgcowk4coocwk
Secret ID: 4gxi02iydlwkwokko4cs8w4skw8goks0s00cw00okosskcg8sg
Chrome Postman.
# Request
GET http://oauth-server.dev/app_dev.php/oauth/v2/token?client_id=8_4mcij3t948isk880w0gskkksc88w0wo0wowogkgcowk4coocwk&client_secret=4gxi02iydlwkwokko4cs8w4skw8goks0s00cw00okosskcg8sg&grant_type=client_credentials
# Response
HTTP/1.1 200 OK
{
"access_token": "ODk5ZTU2MWQ2ZDBiODYxZTU2NjJmMzYyNDk0NTc3YzMyY2Q0NzNmMTQ2NDAzYmFhZTAxNTNmZmEwYmIxNWRlZA",
"expires_in": 3600,
"token_type": "bearer",
"scope": null
}
Curl.
# Request
curl 'http://oauth-server.dev/app_dev.php/oauth/v2/token?client_id=8_4mcij3t948isk880w0gskkksc88w0wo0wowogkgcowk4coocwk&client_secret=4gxi02iydlwkwokko4cs8w4skw8goks0s00cw00okosskcg8sg&grant_type=client_credentials'
# Response
HTTP/1.1 200 OK
{
"access_token": "MjA5ZDQ0MTFhZGM5OTJiN2VmNGViNWNiMDFiZTM4YzMzMTcxMDU3NjI3M2Q0NjU2NWU1MGY0MDUxOTk5ZTQ3YQ",
"expires_in": 3600,
"token_type": "bearer",
"scope": null
}
Symfony.
public function indexAction()
{
$oauthHeaders = [
"client_id" => "8_4mcij3t948isk880w0gskkksc88w0wo0wowogkgcowk4coocwk",
"client_secret" => "4gxi02iydlwkwokko4cs8w4skw8goks0s00cw00okosskcg8sg",
"grant_type" => "client_credentials"
];
$endpoint = "http://oauth-server.dev/app_dev.php/oauth/v2/token";
$curl = curl_init($endpoint);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, 'Content-Type: application/x-www-form-urlencoded');
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($oauthHeaders));
$response = curl_exec($curl);
curl_close($curl);
return new Response($response);
}
# Response
HTTP/1.1 200 OK
{
"access_token": "NmRmNjBjMTQyNzdiOGI5ZDIwZmQyN2IwZjcxMDUxMDRiZjBhMDUyMmMxNmYzMGYxYTNhOTIwNjhlZDVmM2M5Mg",
"expires_in": 3600,
"token_type": "bearer",
"scope": null
}
Curl.
# Request
curl -H "Authorization: Bearer NTU2ODcyYmIzNDM5N2M2YWRiYzAyNTk5NDI0OGI0ZjM2ZGFmZTgyYjZiZmJhOGEwN2RjNWEzMzIxNzQyNzQ3ZA" http://oauth-server.dev/app_dev.php/1/server/team
# Response
HTTP/1.1 200 OK
GET your team from Server
# Request
curl -v -X POST -H 'Authorization: Bearer NTU2ODcyYmIzNDM5N2M2YWRiYzAyNTk5NDI0OGI0ZjM2ZGFmZTgyYjZiZmJhOGEwN2RjNWEzMzIxNzQyNzQ3ZA' 'Content-Type:application/json' -d '{"hello":"world"}' 'http://oauth-server.dev/app_dev.php/1/server/team'
# Response
HTTP/1.1 200 OK
POST your team to Server
Symfony.
public function indexAction()
{
$oauthHeaders = [
'Authorization: Bearer NTU2ODcyYmIzNDM5N2M2YWRiYzAyNTk5NDI0OGI0ZjM2ZGFmZTgyYjZiZmJhOGEwN2RjNWEzMzIxNzQyNzQ3ZA'
];
$endpoint = "http://oauth-server.dev/app_dev.php/1/server/team";
$curl = curl_init($endpoint);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $oauthHeaders);
$response = curl_exec($curl);
curl_close($curl);
return new Response($response);
}
# Response
HTTP/1.1 200 OK
GET your team from Server
# Request
public function indexAction(Request $request)
{
$oauthHeaders = [
'Authorization: Bearer NTU2ODcyYmIzNDM5N2M2YWRiYzAyNTk5NDI0OGI0ZjM2ZGFmZTgyYjZiZmJhOGEwN2RjNWEzMzIxNzQyNzQ3ZA'
];
$endpoint = "http://oauth-server.dev/app_dev.php/1/server/team";
$curl = curl_init($endpoint);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $oauthHeaders);
curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getContent());
$response = curl_exec($curl);
curl_close($curl);
return new Response($response);
}
# Response
HTTP/1.1 200 OK
POST your team to Server
Süresi bitmiş bir token ile request göndermek.
# Request
curl -v -H "Authorization: Bearer ZDc3ZDgyYzRmYjQ2MzdiMDJkYjc3N2UzYTMyNDg1NjkyYzFiNjUyMGQ2NjYwMTNlMjEwYzZlMzBmODZjN2EwYQ" http://oauth-server.dev/app_dev.php/1/server/team
# Response
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to oauth-server.local (127.0.0.1) port 80 (#0)
> GET /app_dev.php/1/server/team HTTP/1.1
> User-Agent: curl/7.37.1
> Host: oauth-server.local
> Accept: */*
> Authorization: Bearer ZDc3ZDgyYzRmYjQ2MzdiMDJkYjc3N2UzYTMyNDg1NjkyYzFiNjUyMGQ2NjYwMTNlMjEwYzZlMzBmODZjN2EwYQ
>
< HTTP/1.1 401 Unauthorized
< Date: Tue, 29 Dec 2015 14:52:17 GMT
* Server Apache/2.4.10 (Unix) PHP/5.6.9 is not blacklisted
< Server: Apache/2.4.10 (Unix) PHP/5.6.9
< Vary: Authorization
< X-Powered-By: PHP/5.6.9
< WWW-Authenticate: Bearer realm="Service", error="invalid_grant", error_description="The access token provided has expired."
< Cache-Control: no-store, private
< Pragma: no-cache
< X-Debug-Token: 532cf9
< X-Debug-Token-Link: /app_dev.php/_profiler/532cf9
< Content-Length: 86
< Content-Type: application/json
<
* Connection #0 to host oauth-server.local left intact
{"error":"invalid_grant","error_description":"The access token provided has expired."}
Daha fazla bilgi için, aşağıdaki linkleri kontrol edebilirsiniz.