タイトルが言っているように、APIプラットフォームとlexikjwtauthenticationbundleを使用して、非常にシンプルなJWT認証を作成します。もちろん、私たちの素敵なDoctrineユーザープロバイダーを使用します。
JWTとのSymfonyについてのオンラインでのチュートリアルが多すぎます。また、Api-platformについてもあります。しかし、それらのほとんどは本当に役に立たないか、非常に短いものであり、私が見つけたそれらのほとんどは多くのことを見逃していました。彼らはあなたが最初にいくつかの概念を知る必要があるとは言わず、最後にあなたはそれのために非常に多くの混乱した開発者を見つけることができます。
または、彼らはAPIプラットフォーム名に言及しているだけで、あなたは入力しますが、それはほとんどがSymfonyについてではありません。正直に言うと、APIプラットフォームコミュニティは小さすぎてSymfonyに基づいているため、それは理にかなっています。これは、Symfonyコミュニティからすでに尋ねられ、解決した問題のほとんどが非常に大きなコミュニティです。
これが異なることを願っています。懸念や更新があれば大歓迎です。また、どんな質問もそれらのすべてに答えようとします。
私がそれをインストールするための最良の方法は、gitリポジトリを使用するか、githubから.zipファイルとしてAPIプラットフォームをダウンロードすることです。
$ 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に設定したように、今は誰にもこの2つのAPIにアクセスしてもらいたくありません。また、以前に定義したユーザープロバイダーであることをフレームワークに伝えます。
ここで、あなたが電話を試した場合、私たちは /GreetingsAPIの追加部品でそれを試しました
$ 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トークンからのみ見ることができるように、Whosがログインしていることを正確に知ることができ、Isactiveやuserrolesなどの添加物ユーザープロパティを実装することでこれを改善できます。