I. Introduction▲
Laravel 5 arrive avec une infrastructure pour l'authentification. On trouve dans l'installation de base deux contrôleurs :
Et deux middlewares (logiciels médiateurs) :
Et c'est tout ! Il faut donc créer tout le reste.
II. Les routes▲
Avec Laravel 5.2 on peut obtenir toutes les routes de l'authentification avec php artisan make:auth, mais on peut aussi les écrire, ce que j'ai préféré faire :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
// Authentication routes...
Route::
get('
auth/login
'
,
'
Auth\AuthController@getLogin
'
);
Route::
post('
auth/login
'
,
'
Auth\AuthController@postLogin
'
);
Route::
get('
auth/logout
'
,
'
Auth\AuthController@getLogout
'
);
Route::
get('
auth/confirm/{token}
'
,
'
Auth\AuthController@getConfirm
'
);
// Renvoyer des routes...
Route::
get('
auth/resend
'
,
'
Auth\AuthController@getResend
'
);
// Routes d'inscription...
Route::
get('
auth/register
'
,
'
Auth\AuthController@getRegister
'
);
Route::
post('
auth/register
'
,
'
Auth\AuthController@postRegister
'
);
// Routes de mot de passe réinitialisé...
Route::
get('
password/email
'
,
'
Auth\PasswordController@getEmail
'
);
Route::
post('
password/email
'
,
'
Auth\PasswordController@postEmail
'
);
// Routes de réinitialisation de mot de passe ...
Route::
get('
password/reset/{token}
'
,
'
Auth\PasswordController@getReset
'
);
Route::
post('
password/reset
'
,
'
Auth\PasswordController@postReset
'
);
Notez que la route pour la confirmation de l'email est forcément à créer.
III. La validation▲
Comme j'ai dû surcharger les méthodes correspondantes des contrôleurs, j'ai créé des requêtes de formulaires :
Ces requêtes ne présentent aucune difficulté ou particularité, je vous laisse donc les consulter si besoin.
IV. Les contrôleurs▲
IV-A. AuthController▲
Lorsque l'on regarde ce contrôleur dans l'installation de base on n'y trouve pas grand-chose :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
<?php
namespace
App\Http\Controllers\Auth;
use
App\User;
use
Validator;
use
App\Http\Controllers\Controller;
use
Illuminate\Foundation\Auth\ThrottlesLogins;
use
Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class
AuthController extends
Controller
{
/*
|--------------------------------------------------------------------------
| Contrôleur d'inscription et de connexion
|--------------------------------------------------------------------------
|
| Ce contrôleur gère l'enregistrement des nouveaux utilisateurs, ainsi que
|l'authentification des utilisateurs existants. Par défaut, ce contrôleur
|utilise un trait simple pour ajouter ces comportements. Pourquoi ne pas
|l'explorer?
|
|
|
*/
use
AuthenticatesAndRegistersUsers,
ThrottlesLogins;
/**
*
*Créer une nouvelle instance de contrôleur d'authentification.
*
*
@return
void
*/
public
function
__construct
()
{
$this
->
middleware('guest'
,
[
'except'
=>
'getLogout'
]
);
}
/**
* Obtenir un validateur pour une demande d'inscription entrante.
*
*
@param
array
$data
*
@return
\Illuminate\Contracts\Validation\Validator
*/
protected
function
validator(array
$data
)
{
return
Validator::
make($data
,
[
'name'
=>
'required|max:255'
,
'email'
=>
'required|email|max:255|unique:users'
,
'password'
=>
'required|confirmed|min:6'
,
]
);
}
/**
* Créer une nouvelle instance d'utilisateur après une inscription valide.
*
*
@param
array
$data
*
@return
User
*/
protected
function
create(array
$data
)
{
return
User::
create([
'name'
=>
$data
[
'name'
],
'email'
=>
$data
[
'email'
],
'password'
=>
bcrypt($data
[
'password'
]
),
]
);
}
}
En fait juste un constructeur, un validateur et du code pour la création d'un utilisateur. L'essentiel du traitement est effectué dans le trait (similaire à une classe) AuthenticatesAndRegistersUsers. Comme ce trait est dans le core il est évidemment déconseillé d'aller modifier son code. Il est prévu quelques fonctionnalités concernant les URL, mais au-delà de ces possibilités, la seule solution pour adapter le code consiste à surcharger des méthodes du trait.
IV-A-1. postLogin▲
J'ai choisi de surcharger cette méthode pour plusieurs raisons :
- j'ai donné la possibilité de se connecter à partir du pseudo ou de l'email ;
- je voulais vérifier que l'utilisateur avait confirmé son adresse email ;
- je voulais gérer facilement la redirection et le message d'erreur.
Ce qui donne finalement ce code :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
public function postLogin(
LoginRequest $request
,
Guard $auth
)
{
$logValue
=
$request
->
input('
log
'
);
$logAccess
=
filter_var($logValue
,
FILTER_VALIDATE_EMAIL) ?
'
email
'
:
'
username
'
;
$throttles
=
in_array(
ThrottlesLogins::
class,
class_uses_recursive(get_class($this
))
);
if ($throttles
&&
$this
->
hasTooManyLoginAttempts($request
)) {
return redirect('
/auth/login
'
)
->
with('
error
'
,
trans('
front/login.maxattempt
'
))
->
withInput($request
->
only('
log
'
));
}
$credentials
=
[
$logAccess
=>
$logValue
,
'
password
'
=>
$request
->
input('
password
'
)
];
if(!
$auth
->
validate($credentials
)) {
if ($throttles
) {
$this
->
incrementLoginAttempts($request
);
}
return redirect('
/auth/login
'
)
->
with('
error
'
,
trans('
front/login.credentials
'
))
->
withInput($request
->
only('
log
'
));
}
$user
=
$auth
->
getLastAttempted();
if($user
->
confirmed) {
if ($throttles
) {
$this
->
clearLoginAttempts($request
);
}
$auth
->
login($user
,
$request
->
has('
memory
'
));
if($request
->
session()->
has('
user_id
'
)) {
$request
->
session()->
forget('
user_id
'
);
}
return redirect('
/
'
);
}
$request
->
session()->
put('
user_id
'
,
$user
->
id);
return redirect('
/auth/login
'
)->
with('
error
'
,
trans('
front/verify.again
'
));
}
La principale particularité réside dans la double possibilité de connexion. Pour distinguer les deux je me suis fondé sur le type d'information.
IV-A-2. postRegister▲
J'ai surchargé cette méthode pour utiliser une requête de formulaire, créer un code de confirmation pour l'adresse email, flasher un message pour l'utilisateur :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
public function postRegister(
RegisterRequest $request
,
UserRepository $user_gestion
)
{
$user
=
$user_gestion
->
store(
$request
->
all(),
$confirmation_code
=
str_random(30
)
);
$this
->
dispatch(new SendMail($user
));
return redirect('
/
'
)->
with('
ok
'
,
trans('
front/verify.message
'
));
}
IV-B. PasswordController▲
Ce contrôleur est très maigre dans l'installation de base :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
<?php
namespace
App\Http\Controllers\Auth;
use
App\Http\Controllers\Controller;
use
Illuminate\Foundation\Auth\ResetsPasswords;
class
PasswordController extends
Controller
{
/*
|--------------------------------------------------------------------------
| Contrôleur de réinitialisation de mot de passe
|--------------------------------------------------------------------------
|
| Ce contrôleur est responsable du traitement des demandes de
|réinitialisation de mot de passe et utilise un trait simple pour inclure
|ce comportement. Vous êtes libre d'explorer ce trait et d'ignorer les
|méthodes que vous souhaitez modifier. |
*/
use
ResetsPasswords;
/**
* Créer une nouvelle instance de contrôleur de mot de passe.
*
*
@return
void
*/
public
function
__construct
()
{
$this
->
middleware('guest'
);
}
}
L'essentiel du code est également dans un trait : ResetsPasswords. Le raisonnement est encore le même qu'avec le précédent contrôleur.
Pour ce contrôleur j'ai donc surchargé les principales méthodes.
IV-B-1. postEmail▲
Là, je devais gérer le contenu de l'email selon la langue, pour y parvenir je suis passé par un composeur de vues. D'autre part, j'ai utilisé une requête de formulaire pour la validation :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
public function postEmail(
EmailPasswordLinkRequest $request
,
Factory $view
)
{
$view
->
composer('
emails.auth.password
'
,
function($view
) {
$view
->
with([
'
title
'
=>
trans('
front/password.email-title
'
),
'
intro
'
=>
trans('
front/password.email-intro
'
),
'
link
'
=>
trans('
front/password.email-link
'
),
'
expire
'
=>
trans('
front/password.email-expire
'
),
'
minutes
'
=>
trans('
front/password.minutes
'
),
]
);
}
);
$response
=
Password::
sendResetLink($request
->
only('
email
'
),
function (Message $message
) {
$message
->
subject(trans('
front/password.reset
'
));
}
);
switch ($response
) {
case Password::
RESET_LINK_SENT:
return redirect()->
back()->
with('
status
'
,
trans($response
));
case Password::
INVALID_USER:
return redirect()->
back()->
with('
error
'
,
trans($response
));
}
}
IV-B-2. postReset▲
Ici ma seule justification est d'utiliser une requête de formulaire.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
public function postReset(ResetPasswordRequest $request
)
{
$credentials
=
$request
->
only(
'
email
'
,
'
password
'
,
'
password_confirmation
'
,
'
token
'
);
$response
=
Password::
reset
($credentials
,
function($user
,
$password
) {
$this
->
resetPassword($user
,
$password
);
}
);
switch ($response
) {
case Password::
PASSWORD_RESET:
return redirect()->
to('
/
'
)->
with('
ok
'
,
trans('
passwords.reset
'
));
default:
return redirect()->
back()
->
with('
error
'
,
trans($response
))
->
withInput($request
->
only('
email
'
));
}
}
V. Les vues▲
C'est au niveau des vues qu'a porté le plus gros des adaptations pour obtenir un visuel bien intégré au site.
V-A. Le login▲
Voici le visuel :
On trouve un champ de saisie pour le pseudo ou l'email (au choix) et un autre pour le mot de passe. Il est prévu aussi une case à cocher pour la création d'un cookie pour mémoriser la connexion. D'autre part, un lien est prévu pour l'oubli du mot de passe. Dans la partie inférieure on propose une inscription.
Le code de la vue (login.blade.php) est optimisé grâce à des méthodes ajoutées au FormBuilder (je détaillerai cet aspect dans un article ultérieur) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
@extends
('front.template'
)
@section
('main'
)
<
div class
=
"
row
"
>
<
div class
=
"
box
"
>
<
div class
=
"
col-lg-12
"
>
@if
(session()->
has('error'
))
@
include('partials/error'
,
[
'type'
=>
'danger'
,
'message'
=>
session('error'
)]
)
@endif
<
hr>
<
h2 class
=
"
intro-text text-center
"
>
{{
trans('front/login.connection'
) }}
<
/h2
>
<
hr>
<
p>
{{
trans('front/login.text'
) }}
<
/p
>
{!!
Form::
open([
'url'
=>
'auth/login'
,
'method'
=>
'post'
,
'role'
=>
'form'
]
) !!}
<
div class
=
"
row
"
>
{!!
Form::
control('text'
,
6
,
'log'
,
$errors
,
trans('front/login.log'
)) !!}
{!!
Form::
control('password'
,
6
,
'password'
,
$errors
,
trans('front/login.password'
)) !!}
{!!
Form::
submit(trans('front/form.send'
),
[
'col-lg-12'
]
) !!}
{!!
Form::
check('memory'
,
trans('front/login.remind'
)) !!}
{!!
Form::
text('address'
,
''
,
[
'class'
=>
'hpet'
]
) !!}
<
div class
=
"
col-lg-12
"
>
{!!
link_to('password/email'
,
trans('front/login.forget'
)) !!}
<
/div
>
<
/div
>
{!!
Form::
close() !!}
<
div class
=
"
text-center
"
>
<
hr>
<
h2 class
=
"
intro-text text-center
"
>
{{
trans('front/login.register'
) }}
<
/h2
>
<
hr>
<
p>
{{
trans('front/login.register-info'
) }}
<
/p
>
{!!
link_to('auth/register'
,
trans('front/login.registering'
),
[
'class'
=>
'btn btn-default'
]
) !!}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
@stop
V-B. L'inscription▲
Voici le visuel :
Les champs de saisie sont prévus pour :
- le pseudo ;
- l'email ;
- le mot de passe ;
- la confirmation du mot de passe.
J'ai aussi prévu des bulles d'information pour faciliter la saisie :
Ici aussi le code du formulaire est simplifié avec les méthodes ajoutées (register.blade.php) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
@extends
('front.template'
)
@section
('main'
)
<
div class
=
"
row
"
>
<
div class
=
"
box
"
>
<
div class
=
"
col-lg-12
"
>
<
hr>
<
h2 class
=
"
intro-text text-center
"
>
{{
trans('front/register.title'
) }}
<
/h2
>
<
hr>
<
p>
{{
trans('front/register.infos'
) }}
<
/p
>
{!!
Form::
open([
'url'
=>
'auth/register'
,
'method'
=>
'post'
,
'role'
=>
'form'
]
) !!}
<
div class
=
"
row
"
>
{!!
Form::
control('text'
,
6
,
'username'
,
$errors
,
trans('front/register.pseudo'
),
null
,
[
trans('front/register.warning'
),
trans('front/register.warning-name'
)]
) !!}
{!!
Form::
control('email'
,
6
,
'email'
,
$errors
,
trans('front/register.email'
)) !!}
<
/div
>
<
div class
=
"
row
"
>
{!!
Form::
control('password'
,
6
,
'password'
,
$errors
,
trans('front/register.password'
),
null
,
[
trans('front/register.warning'
),
trans('front/register.warning-password'
)]
) !!}
{!!
Form::
control('password'
,
6
,
'password_confirmation'
,
$errors
,
trans('front/register.confirm-password'
)) !!}
<
/div
>
{!!
Form::
text('address'
,
''
,
[
'class'
=>
'hpet'
]
) !!}
<
div class
=
"
row
"
>
{!!
Form::
submit(trans('front/form.send'
),
[
'col-lg-12'
]
) !!}
<
/div
>
{!!
Form::
close() !!}
<
/div
>
<
/div
>
<
/div
>
@stop
@section
('scripts'
)
<script>
$(
function
(
) {
$(
'
.badge
'
).popover
(
);
}
);
</
script>
@stop
Lorsque quelqu'un s'inscrit il reçoit ce message :
En effet on lui a envoyé un email pour la confirmation de l'adresse :
Et quand on clique l'action est gérée par cette méthode du contrôleur :
Si la confirmation est bonne on obtient ce message :
V-C. L'oubli du mot de passe▲
Voici le visuel :
Un petit texte explicatif et un champ de saisie pour l'email. Le code est encore plus simple (password.blade.php) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
@extends
('front.template'
)
@section
('main'
)
<
div class
=
"
row
"
>
<
div class
=
"
box
"
>
<
div class
=
"
col-lg-12
"
>
@if
(session()->
has('status'
))
@
include('partials/error'
,
[
'type'
=>
'success'
,
'message'
=>
session('status'
)]
)
@endif
@if
(session()->
has('error'
))
@
include('partials/error'
,
[
'type'
=>
'danger'
,
'message'
=>
session('error'
)]
)
@endif
<
hr>
<
h2 class
=
"
intro-text text-center
"
>
{{
trans('front/password.title'
) }}
<
/h2
>
<
hr>
<
p>
{{
trans('front/password.info'
) }}
<
/p
>
{!!
Form::
open([
'url'
=>
'password/email'
,
'method'
=>
'post'
,
'role'
=>
'form'
]
) !!}
<
div class
=
"
row
"
>
{!!
Form::
control('email'
,
6
,
'email'
,
$errors
,
trans('front/password.email'
)) !!}
{!!
Form::
submit(trans('front/form.send'
),
[
'col-lg-12'
]
) !!}
{!!
Form::
text('address'
,
''
,
[
'class'
=>
'hpet'
]
) !!}
<
/div
>
{!!
Form::
close() !!}
<
/div
>
<
/div
>
<
/div
>
@stop
V-D. Le nouveau mot de passe▲
Voici le visuel :
Trois champs de saisie :
- l'email ;
- le mot de passe ;
- la confirmation du mot de passe.
Avec la même organisation du code (reset.blade.php) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
@extends
('front.template'
)
@section
('main'
)
<
div class
=
"
row
"
>
<
div class
=
"
box
"
>
<
div class
=
"
col-lg-12
"
>
@if
(session()->
has('error'
))
@
include('partials/error'
,
[
'type'
=>
'danger'
,
'message'
=>
session('error'
)]
)
@endif
<
hr>
<
h2 class
=
"
intro-text text-center
"
>
{{
trans('front/password.title-reset'
) }}
<
/h2
>
<
hr>
<
p>
{{
trans('front/password.reset-info'
) }}
<
/p
>
{!!
Form::
open([
'url'
=>
'password/reset'
,
'method'
=>
'post'
,
'role'
=>
'form'
]
) !!}
<
div class
=
"
row
"
>
{!!
Form::
hidden('token'
,
$token
) !!}
{!!
Form::
control('email'
,
6
,
'email'
,
$errors
,
trans('front/password.email'
)) !!}
{!!
Form::
control('password'
,
6
,
'password'
,
$errors
,
trans('front/password.password'
),
null
,
[
trans('front/password.warning'
),
trans('front/password.warning-password'
)]
) !!}
{!!
Form::
control('password'
,
6
,
'password_confirmation'
,
$errors
,
trans('front/password.confirm-password'
)) !!}
{!!
Form::
submit(trans('front/form.send'
),
[
'col-lg-12'
]
) !!}
<
/div
>
{!!
Form::
close() !!}
<
/div
>
<
/div
>
<
/div
>
@stop
@section
('scripts'
)
<script>
$(
function
(
) {
$(
'
.badge
'
).popover
(
);
}
);
</
script>
@stop
V-E. L'email▲
L'email reçu par l'utilisateur qui a oublié son mot de passe se présente ainsi :
Avec ce code (emails/auth/password.blade.php) :
On a ainsi fait le tour de tout ce qui concerne l'authentification pour cette application.