كما يقول العنوان أننا سنقوم بإنشاء مصادقة JWT بسيطة للغاية باستخدام منصة API و LexikjwTaUthenticationBundle. باستخدام مزود مستخدم العقيدة الجميل لدينا.
هناك الكثير من البرامج التعليمية عبر الإنترنت حول Symfony مع JWT ، وكذلك بعضها عن منصة API. لكن معظمهم ليسوا مفيدين في الحقيقة أو أنهم قصيرون للغاية ، ومعظمهم وجدت أنه يفتقد أشياء كثيرة. حتى أنهم لا يقولون أنك بحاجة إلى معرفة بعض المفاهيم أولاً وفي النهاية تجد الكثير من المطورين المربكة بسبب ذلك.
أو أنهم يذكرون فقط اسم منصة API وأنت تدخل ولا شيء في الغالب يدور حول Symfony. لكي نكون صادقين ، فهذا أمر منطقي لأن مجتمع منصة API صغير جدًا وكما يعتمد حقًا على Symfony ، لذا فإن معظم القضايا التي يتم طرحها بالفعل وحلها من قبل Symfony Community ، لذلك نحن محظوظون لأنه مجتمع كبير :).
آمل أن يكون هذا مختلفًا وإذا كان لديك أي مخاوف أو تحديثات سيكون موضع تقدير كبير. كما ستحاول أي أسئلة الإجابة عليها جميعًا.
أفضل طريقة بالنسبة لي لتثبيتها هي استخدام مستودع GIT ، أو تنزيل منصة API كملف .zip من Github.
$ git clone https://github.com/api-platform/api-platform.git apiplatform-user-auth
$ cd apiplatform-user-authالآن ، أولاً وقبل كل شيء ، تعمل منصة API بالكامل على منافذ محددة ، لذلك تحتاج إلى التأكد من أن هذه المنافذ مجانية ولا شيء يستمع إليه.
$ sudo lsof -nP | grep LISTEN $ sudo kill -9 $PROCESS_NUMBER docker-compose pulldocker-compose up -d $ docker-compose down
$ COMPOSE_HTTP_TIMEOUT=120 docker-compose up -ddocker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6389d8efb6a0 apiplatform-user-auth_h2-proxy " nginx -g 'daemon of… " About a minute ago Up About a minute 0.0.0.0:443-444- > 443-444/tcp, 80/tcp, 0.0.0.0:8443-8444- > 8443-8444/tcp apiplatform-user-auth_h2-proxy_1_a012bc894b6c
a12ff2759ca4 quay.io/api-platform/varnish " docker-varnish-entr… " 2 minutes ago Up 2 minutes 0.0.0.0:8081- > 80/tcp apiplatform-user-auth_cache-proxy_1_32d747ba8877
6c1d29d1cbdd quay.io/api-platform/nginx " nginx -g 'daemon of… " 2 minutes ago Up 2 minutes 0.0.0.0:8080- > 80/tcp apiplatform-user-auth_api_1_725cd9549081
62f69838dacb quay.io/api-platform/php " docker-entrypoint p… " 2 minutes ago Up 2 minutes 9000/tcp apiplatform-user-auth_php_1_cf09d32c3120
381384222af5 dunglas/mercure " ./mercure " 2 minutes ago Up 2 minutes 443/tcp, 0.0.0.0:1337- > 80/tcp apiplatform-user-auth_mercure_1_54363c253a34
783565efb2eb postgres:10-alpine " docker-entrypoint.s… " 2 minutes ago Up 2 minutes 0.0.0.0:5432- > 5432/tcp apiplatform-user-auth_db_1_8da243ca2865
1bc8e386bf02 quay.io/api-platform/client " /bin/sh -c 'yarn st… " 2 minutes ago Up About a minute 0.0.0.0:80- > 3000/tcp apiplatform-user-auth_client_1_1c413b4e4a5e
c22bef7a0b3f quay.io/api-platform/admin " /bin/sh -c 'yarn st… " 2 minutes ago Up About a minute 0.0.0.0:81- > 3000/tcp apiplatform-user-auth_admin_1_cfecc5c6b442$ docker-compose exec php composer require doctrine maker$ docker-compose exec php bin/console make:user
The name of the security user class (e.g. User) [User]:
> Users
Do you want to store user data in the database (via Doctrine) ? (yes/no) [yes]:
>
Enter a property name that will be the unique " display " name for the user (e.g. email, username, uuid) [email]:
> email
Will this app need to hash/check user passwords ? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).
Does this app need to hash/check user passwords ? (yes/no) [yes]:
>
The newer Argon2i password hasher requires PHP 7.2, libsodium or paragonie/sodium_compat. Your system DOES support this algorithm.
You should use Argon2i unless your production system will not support it.
Use Argon2i as your password hasher (bcrypt will be used otherwise) ? (yes/no) [yes]:
>
created: src/Entity/Users.php
created: src/Repository/UsersRepository.php
updated: src/Entity/Users.php
updated: config/packages/security.yaml
Success !
Next Steps:
- Review your new App E ntity U sers class.
- Use make:entity to add more fields to your Users entity and then run make:migration.
- Create a way to authenticate ! See https://symfony.com/doc/current/security.html # api/src/Entity/Users.php
/**
* @see UserInterface
*/ # api/src/Entity/Users.php
/**
* @ORMEntity(repositoryClass="AppRepositoryUsersRepository")
*/
class Users implements UserInterface
{
...
}يمكنك أن ترى بوضوح في هذا المستودع بعض الوظائف التي تم تنفيذها مسبقًا مثل FindById () ، ولكن الآن دعنا ننشئ وظيفة أخرى تساعدنا على إنشاء مستخدم جديد.
# api/src/Repository/UsersRepository.php
class UsersRepository extends ServiceEntityRepository
{
/** EntityManager $manager */
private $ manager ;
. . . .
}وتهيئته في المنشئ مثل ما يلي:
# api/src/Repository/UsersRepository.php
/**
* UsersRepository constructor.
* @param RegistryInterface $registry
*/
public function __construct ( RegistryInterface $ registry )
{
parent :: __construct ( $ registry , Users::class);
$ this -> manager = $ registry -> getEntityManager ();
} # api/src/Repository/UsersRepository.php
/**
* Create a new user
* @param $data
* @return Users
* @throws DoctrineORMORMException
* @throws DoctrineORMOptimisticLockException
*/
public function createNewUser ( $ data )
{
$ user = new Users ();
$ user -> setEmail ( $ data [ ' email ' ])
-> setPassword ( $ data [ ' password ' ]);
$ this -> manager -> persist ( $ user );
$ this -> manager -> flush ();
return $ user ;
}$ docker-compose exec php bin/console make:controller
Choose a name for your controller class (e.g. TinyJellybeanController):
> AuthController
created: src/Controller/AuthController.php
created: templates/auth/index.html.twig
Success !
Next: Open your new controller class and add some pages ! # api/src/Controller/AuthController.php
use App Repository UsersRepository ;
class AuthController extends AbstractController
{
/** @var UsersRepository $userRepository */
private $ usersRepository ;
/**
* AuthController Constructor
*
* @param UsersRepository $usersRepository
*/
public function __construct ( UsersRepository $ usersRepository )
{
$ this -> usersRepository = $ usersRepository ;
}
. . . . . . .
} # api/config/services.yaml
services :
......
# Repositories
app.user.repository :
class : AppRepositoryUsersRepository
arguments :
- SymfonyBridgeDoctrineRegistryInterface
# Controllers
app.auth.controller :
class : AppControllerAuthController
arguments :
- ' @app.user.repository ' # api/src/Controller/AuthController.php
# Import those
use Symfony Component HttpFoundation Request ;
use Symfony Component HttpFoundation Response ;
# Then add this to the class
/**
* Register new user
* @param Request $request
*
* @return Response
*/
public function register ( Request $ request )
{
$ newUserData [ ' email ' ] = $ request -> get ( ' email ' );
$ newUserData [ ' password ' ] = $ request -> get ( ' password ' );
$ user = $ this -> usersRepository -> createNewUser ( $ newUserData );
return new Response ( sprintf ( ' User %s successfully created ' , $ user -> getUsername ()));
} # src/config/routes.yaml
# Register api
register :
path : /register
controller : AppControllerAuthController::register
methods : ['POST']$ docker-compose exec php bin/console make:migration
$ docker-compose exec php bin/console doctrine:migrations:migrate
WARNING ! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue ? (y/n) y$ curl -X POST -H " Content-Type: application/json " " http://localhost:8080/[email protected]&password=test1 "
User [email protected] successfully created$ docker-compose exec db psql -U api-platform api
psql (10.8)
Type " help " for help.
$ api= # select * from users;
id | email | roles | password
----+----------------+-------+----------
6 | [email protected] | [] | test1
(1 row) # api/src/Repository/UsersRepository.php
use Symfony Component Security Core Encoder UserPasswordEncoderInterface ;
class UsersRepository extends ServiceEntityRepository
{
.......
/** UserPasswordEncoderInterface $encoder */
private $ encoder ;
/**
* UserRepository constructor.
* @param RegistryInterface $registry
* @param UserPasswordEncoderInterface $encoder
*/
public function __construct ( RegistryInterface $ registry , UserPasswordEncoderInterface $ encoder )
{
parent :: __construct ( $ registry , Users::class);
$ this -> manager = $ registry -> getEntityManager ();
$ this -> encoder = $ encoder ;
}
} # api/config/services.yaml
services :
.......
# Repositories
app.user.repository :
class : AppRepositoryUsersRepository
arguments :
- SymfonyBridgeDoctrineRegistryInterface
- SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface # api/src/Repository/UsersRepository.php
public function createNewUser ( $ data )
{
$ user = new Users ();
$ user -> setEmail ( $ data [ ' email ' ])
-> setPassword ( $ this -> encoder -> encodePassword ( $ user , $ data [ ' password ' ]));
. . . . . . .
}$ curl -X POST -H " Content-Type: application/json " " http://localhost:8080/[email protected]&password=test2 "
User [email protected] successfully created$ api= # select * from users;
id | email | roles | password
----+----------------+-------+-------------------------------------------------------------------------------------------------
6 | [email protected] | [] | test1
7 | [email protected] | [] | $argon2i$v =19 $m =1024,t=2,p=2 $VW9tYXEzZHp5U0RMSE5ydA$bo +V1X6rgYZ4ebN/bs1cpz+sf+DQdx3Duu3hvFUII8M
(2 rows)$ docker-compose exec php composer require jwt-auth$ curl -X GET -H " Content-Type: application/json " " http://localhost:8080/greetings "
{
" @context " : " /contexts/Greeting " ,
" @id " : " /greetings " ,
" @type " : " hydra:Collection " ,
" hydra:member " : [],
" hydra:totalItems " : 0
} # api/src/Controller/AuthController.php
/**
* api route redirects
* @return Response
*/
public function api ()
{
return new Response ( sprintf ( " Logged in as %s " , $ this -> getUser ()-> getUsername ()));
} # api/config/routes.yaml
api :
path : /api
controller : AppControllerAuthController::api
methods : ['POST']الآن ، نحتاج إلى إجراء بعض التكوينات في ملف تكوين الأمان الخاص بنا:
# api/config/packages/security.yaml
app_user_provider :
entity :
class : AppEntityUsers
property : email # api/config/packages/security
register :
pattern : ^/register
stateless : true
anonymous : true # api/config/packages/security.yaml
main :
anonymous : false
stateless : true
provider : app_user_provider
json_login :
check_path : /login
username_path : email
password_path : password
success_handler : lexik_jwt_authentication.handler.authentication_success
failure_handler : lexik_jwt_authentication.handler.authentication_failure
guard :
authenticators :
- lexik_jwt_authentication.jwt_token_authenticator # api/config/packages/security.yaml
api :
pattern : ^/api
stateless : true
anonymous : false
provider : app_user_provider
guard :
authenticators :
- lexik_jwt_authentication.jwt_token_authenticatorكما ترى في التكوينات المذكورة أعلاه ، قمنا بتعيين مجهول الهوية على FALSE ، لا نريد أن يصل أي شخص إلى واجهات برمجة التطبيقات هذه الآن ، كما نخبر الإطار عن المزود لك هو مزود المستخدم الذي حددناه من قبل ، وفي النهاية نخبرها بالموزع الذي ستستخدمه ورسائل نجاح المصادقة/المصادقة.
الآن ، إذا جربت المكالمة ، فقد جربناها في الجزء الإضافي لـ /Greetings API
$ curl -X GET -H " Content-Type: application/json " " http://localhost:8080/greetings "
{
" code " : 401,
" message " : " JWT Token not found "
}$ curl -X POST -H " Content-Type: application/json " " http://localhost:8080/api "
{
" code " : 401,
" message " : " JWT Token not found "
} # api/config/packages/security.yaml
... .
json_login :
check_path : /login
# api/config/routes.yaml
# Login check to log the user and generate JWT token
api_login_check :
path : /login
methods : ['POST']$ curl -X POST -H " Content-Type: application/json " http://localhost:8080/login -d ' {"email":"[email protected]","password":"test2"} '
{ " token " : " eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NTg2OTg4MTIsImV4cCI6MTU1ODcwMjQxMiwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoidGVzdDJAbWFpbC5jb20ifQ.nzd5FVhcyrfjYyN8jRgYFp3VOB2QytnPPRGNyp4ZtfLx6IRwg0TWZJPu5OFtOKPkdLO8DQAr_4Fpq_G6oPjzoxmGOASNuRoQonik9FCCq6oAIW3k5utzQecXDVE_ImnfgByc6WYW6a-aWLnsq1qtvxy274ojqdR0rWLePwSWX5K5-t08zDBgavO_87dVpYd0DLwhHIS7F10lNscET7bfWS-ioPDTv-G74OvkcpbcjgwHhXlO7TYubnrES-FsvAw7kezQe4BPxdbXr1w-XBZuqTNEU4MyrBuadSLgjoe_gievNBtkVhKErIkEQZVjeJIQ4xaKaxwmPxZcP9jYkE47myRdbMsL9XHSd0XmGq0bPuGjOJ2KLTmUb5oeuRnY-e9Q_V9BbouEGw0sjw2meo6Jot2MZyv5ZnLci_GwpRtWqmV7ZLw5jNyiLDFXR1rz70NcJh7EXqu9o4nno3oc68zokfDQvGkJJJZMtBrLCK5pKGMh0a1elIz41LRLZvpLYCrOZ2f4wCkGRD_U92iILD6w8EdVWGoO1wTn5Z2k8-GS1-QH9f-4KkOpaYGPCwwdrY7yioSt2oVbEj2FOb1jULteeP_Cpu44HyJktPLPW_wrN2OtZlUFr4Vz_owDSIvNESYk1JBQ_Fjlv9QGmUs9itzaDExjfB4QYoGkvpfNymtw2PI " }كما تراه إنشاء الرمز المميز JWT بالنسبة لي ، حتى أتمكن من استخدامه للاتصال بأي واجهة برمجة تطبيقات في التطبيق. إذا أظهر بعض الاستثناءات مثل غير قادر على إنشاء الرمز المميز للتكوينات المحددة ، فيرجى التحقق من هذه الخطوة هنا. افتح ملف . env أولاً ، سنحتاج إلى jwt_passphrase لذا احتفظ به
$ mkdir -p api/config/jwt
$ openssl genrsa -out api/config/jwt/private.pem -aes256 4096 # this will ask you for the JWT_PASSPHRASE
$ openssl rsa -pubout -in api/config/jwt/private.pem -out api/config/jwt/public.pem # will confirm the JWT_PASSPHRASE again$ curl -X GET -H " Content-Type: application/json " -H " Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NTg2OTg4MTIsImV4cCI6MTU1ODcwMjQxMiwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoidGVzdDJAbWFpbC5jb20ifQ.nzd5FVhcyrfjYyN8jRgYFp3VOB2QytnPPRGNyp4ZtfLx6IRwg0TWZJPu5OFtOKPkdLO8DQAr_4Fpq_G6oPjzoxmGOASNuRoQonik9FCCq6oAIW3k5utzQecXDVE_ImnfgByc6WYW6a-aWLnsq1qtvxy274ojqdR0rWLePwSWX5K5-t08zDBgavO_87dVpYd0DLwhHIS7F10lNscET7bfWS-ioPDTv-G74OvkcpbcjgwHhXlO7TYubnrES-FsvAw7kezQe4BPxdbXr1w-XBZuqTNEU4MyrBuadSLgjoe_gievNBtkVhKErIkEQZVjeJIQ4xaKaxwmPxZcP9jYkE47myRdbMsL9XHSd0XmGq0bPuGjOJ2KLTmUb5oeuRnY-e9Q_V9BbouEGw0sjw2meo6Jot2MZyv5ZnLci_GwpRtWqmV7ZLw5jNyiLDFXR1rz70NcJh7EXqu9o4nno3oc68zokfDQvGkJJJZMtBrLCK5pKGMh0a1elIz41LRLZvpLYCrOZ2f4wCkGRD_U92iILD6w8EdVWGoO1wTn5Z2k8-GS1-QH9f-4KkOpaYGPCwwdrY7yioSt2oVbEj2FOb1jULteeP_Cpu44HyJktPLPW_wrN2OtZlUFr4Vz_owDSIvNESYk1JBQ_Fjlv9QGmUs9itzaDExjfB4QYoGkvpfNymtw2PI " " http://localhost:8080/greetings "
{
" @context " : " /contexts/Greeting " ,
" @id " : " /greetings " ,
" @type " : " hydra:Collection " ,
" hydra:member " : [],
" hydra:totalItems " : 0
}
$ curl -X GET -H " Content-Type: application/json " " http://localhost:8080/greetings "
{
" code " : 401,
" message " : " JWT Token not found "
}أعتقد الآن أنك ترى الاختلاف.
$ curl -X POST -H " Content-Type: application/json " -H " Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NTg2OTg4MTIsImV4cCI6MTU1ODcwMjQxMiwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoidGVzdDJAbWFpbC5jb20ifQ.nzd5FVhcyrfjYyN8jRgYFp3VOB2QytnPPRGNyp4ZtfLx6IRwg0TWZJPu5OFtOKPkdLO8DQAr_4Fpq_G6oPjzoxmGOASNuRoQonik9FCCq6oAIW3k5utzQecXDVE_ImnfgByc6WYW6a-aWLnsq1qtvxy274ojqdR0rWLePwSWX5K5-t08zDBgavO_87dVpYd0DLwhHIS7F10lNscET7bfWS-ioPDTv-G74OvkcpbcjgwHhXlO7TYubnrES-FsvAw7kezQe4BPxdbXr1w-XBZuqTNEU4MyrBuadSLgjoe_gievNBtkVhKErIkEQZVjeJIQ4xaKaxwmPxZcP9jYkE47myRdbMsL9XHSd0XmGq0bPuGjOJ2KLTmUb5oeuRnY-e9Q_V9BbouEGw0sjw2meo6Jot2MZyv5ZnLci_GwpRtWqmV7ZLw5jNyiLDFXR1rz70NcJh7EXqu9o4nno3oc68zokfDQvGkJJJZMtBrLCK5pKGMh0a1elIz41LRLZvpLYCrOZ2f4wCkGRD_U92iILD6w8EdVWGoO1wTn5Z2k8-GS1-QH9f-4KkOpaYGPCwwdrY7yioSt2oVbEj2FOb1jULteeP_Cpu44HyJktPLPW_wrN2OtZlUFr4Vz_owDSIvNESYk1JBQ_Fjlv9QGmUs9itzaDExjfB4QYoGkvpfNymtw2PI " " http://localhost:8080/api "
Logged in as [email protected]كما يمكنك أن ترى فقط من رمز JWT ، يمكنك معرفة من يتم تسجيل الدخول بالضبط ، ويمكنك تحسين ذلك عن طريق تسوية خصائص المستخدم الإضافية مثل Isactive أو Userroles ... إلخ.