ตามที่ชื่อบอกว่าเราจะสร้างการรับรองความถูกต้อง JWT อย่างง่ายโดยใช้แพลตฟอร์ม API และ LexikjwtauthenticationBundle การใช้แน่นอนผู้ให้บริการผู้ใช้หลักคำสอนที่น่ารักของเรา
มีบทช่วยสอนมากเกินไปทางออนไลน์เกี่ยวกับ Symfony กับ JWT และบางอย่างเกี่ยวกับ Api-Platform แต่ส่วนใหญ่ของพวกเขาไม่เป็นประโยชน์จริง ๆ หรือพวกเขาสั้นมากและส่วนใหญ่ของพวกเขาที่ฉันพบว่าหายไปหลายสิ่งหลายอย่าง พวกเขาไม่ได้บอกว่าคุณต้องรู้แนวคิดบางอย่างก่อนและในตอนท้ายคุณจะพบนักพัฒนาที่สับสนมากมายเพราะมัน
หรือพวกเขาเพิ่งพูดถึงชื่อแพลตฟอร์ม API และคุณเข้ามาและไม่มีอะไรส่วนใหญ่เกี่ยวกับ Symfony ความจริงแล้วมันสมเหตุสมผลเพราะชุมชนแพลตฟอร์ม API นั้นเล็กเกินไปและมีพื้นฐานมาจาก Symfony ดังนั้นปัญหาส่วนใหญ่ที่ถามและแก้ไขโดยชุมชน Symfony ดังนั้นเราจึงโชคดีที่มันเป็นชุมชนที่ยิ่งใหญ่ :)
ฉันหวังว่าสิ่งนี้จะแตกต่างกันและหากคุณมีข้อกังวลหรือการอัปเดตใด ๆ จะได้รับการชื่นชมมาก คำถามใด ๆ จะพยายามตอบคำถามทั้งหมด
วิธีที่ดีที่สุดสำหรับฉันในการติดตั้งคือการใช้พื้นที่เก็บข้อมูล 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อย่างที่คุณเห็นในการกำหนดค่าข้างต้นเราตั้งค่า ไม่ระบุชื่อ เป็น เท็จ เราไม่ต้องการให้ใครเข้าถึง API สองตัวนี้ตอนนี้เรายังบอกเฟรมเวิร์กผู้ให้บริการสำหรับคุณคือ ผู้ให้บริการผู้ใช้ ที่เรากำหนดไว้ก่อนหน้านี้และในตอนท้าย
ตอนนี้ถ้าคุณลองสายเราลองในส่วน เพิ่มเติม สำหรับ /ทักทาย 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 สำหรับฉันดังนั้นฉันสามารถใช้มันเพื่อเรียก API ใด ๆ ในแอปพลิเคชัน หากแสดงข้อยกเว้นบางอย่างเช่น ไม่สามารถสร้างโทเค็นสำหรับการกำหนดค่าที่ระบุ โปรดตรวจสอบขั้นตอนนี้ที่นี่ ก่อนอื่นเปิดไฟล์ . 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 เท่านั้นที่คุณสามารถรู้ได้อย่างชัดเจนว่าใครก็ตามที่เข้าสู่ระบบและคุณสามารถปรับปรุงสิ่งนี้ได้โดยการรวมคุณสมบัติของผู้ใช้ additonal เช่น isactive หรือ userroles ... ฯลฯ