Symfony 4 login form with security and database users

£可爱£侵袭症+ 提交于 2019-12-05 03:57:10

问题


I was a total noob on Symfony about a week ago and I thought I should just dive in Symfony 4. After a week of trying to solve the basic login problem, I believe the documentation is still missing some parts.

Now I've found a solution and I will share it along with some tips on what you might be doing wrong. First part of the answer is a list of suggestions, while the second part is the creation of a project with working login from scratch (supposing you already have composer installed and using a server like apache).


回答1:


Part 1: Suggestions

403 Forbidden

Check the access_control: key in security.yaml. The order of the rules has impact, since no more than one rule will match each time. Keep most specific rules on top.

login_check

Make sure the form action sends you to the login_check path, or whatever you changed it to in security.yaml.

Also check that you have declared a route for the login_check path either in a controller or in routes.yaml.

input name

Symfony forms tend to encapsulate input names in an array, while it only expects them to be named _username and _password (you can change that in security.yaml) to count it as a login attempt. So inspect the inputs to make sure the name attributes are correct.

Part 2: Full Symfony 4 Login

Project Setup

Let's start by creating the project. Open cmd/terminal and go to the folder you want to contain the project folder.

cd .../MyProjects
composer create-project symfony/website-skeleton my-project
cd my-project

Now you have created a Symfony 4 website template in .../MyProjects/my-project and the cmd/terminal is in that path and will execute the rest of the commands properly.

Check in your .../MyProjects/my-project/public folder for a .htaccess file. If it exists you are fine, else run the following command.

composer require symfony/apache-pack

You can now find your site by visiting my-project.dev/public. If you want to remove this public path, you should do so using the .htaccess file, not moving the index.php.

Project Settings

1) Edit the DATABASE_URL key inside the .env file to correspond to your database settings.

2) Edit the config/packages/security.yaml file, so it looks like this:

security:
    encoders:
        App\Entity\User:
            algorithm: bcrypt
    providers:
        user:
            entity:
                class: App\Entity\User
                property: username
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            provider: user
            form_login:
                #login_path: login
                #check_path: login_check
                default_target_path: homepage
                #username_parameter: _username
                #password_parameter: _password
            logout:
                #path:   /logout
                #target: /
    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/,      roles: ROLE_USER }
        - { path: ^/admin, roles: ROLE_ADMIN }

Some explanation:

App\Entity\User is the User entity you 'll create in a while to handle the login.

The user provider is just a name that needs to have a match in providers and firewalls.

The logout key must be declared if you want to allow the user to... well, logout.

Values in #comment reveal the default value we'll be using later on and act as a reference of what you are more likely to change.

User Entity

A user must have a role, but could have more. So let's build a UserRole Entity first for a ManyToMany relationship.

php bin/console make:entity userRole

All entities start with an id property. Add a role too.

php bin/console make:entity user

User needs the username, password and roles properties, but you can add more.

Let's edit the src/Entity/User.php file:

Add the UserInterface interface to your User class.

use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface

Edit the generated getRoles(), to make it return string array.

public function getRoles(): array
{
    $roles = $this->roles->toArray();
    foreach($roles as $k => $v) {
        $roles[$k] = $v->getRole();
    }
    return $roles;
}

getSalt() and eraseCredentials() are functions to implement the UserInterface interface.

public function getSalt()
{
    return null;
}
public function eraseCredentials()
{
}

Using the bcrypt algorithm (as we set in security.yaml) we don't need a salt. It generates automatically one. No, you don't store this salt anywhere and yes, it will produce different hash for the same password every time. But yes, it will work somehow (magic...).

If you need a different algorithm, that uses salt, you need to add a salt property on the User entity.

Homepage

For testing purposes we will create a homepage

php bin/console make:controller homepage

Edit the generated src/Controller/HomepageController.php file to change the root to /

@Route("/", name="homepage")

Login Controller

php bin/console make:controller login

Edit the generated src/Controller/LoginController.php file to make it like this:

<?php

namespace App\Controller;

use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use App\Form\LoginType;

class LoginController extends Controller
{
    /**
     * @Route("/login", name="login")
     */
    public function index(AuthenticationUtils $authenticationUtils)
    {
        $error = $authenticationUtils->getLastAuthenticationError();
        $lastUsername = $authenticationUtils->getLastUsername();
        $form = $this->createForm(LoginType::class);
        return $this->render('login/index.html.twig', [
            'last_username' => $lastUsername,
            'error'         => $error,
            'form'          => $form->createView(),
        ]);
    }

    /**
     * @Route("/logout", name="logout")
     */
    public function logout() {}

    /**
     * @Route("/login_check", name="login_check")
     */
    public function login_check() {}
}

Login Form

php bin/console make:form login

You don't have to associate it to the User entity.

Edit the generated src/Form/LoginType.php file to add this:

use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

replace this:

$builder
    ->add('_username')
    ->add('_password', PasswordType::class)
    ->add('login', SubmitType::class, ['label' => 'Login'])
;

and add this function, to prevent Symfony from changing the input names you requested above by enclosing them in login[...]

public function getBlockPrefix() {}

Login Template

Edit the templates/login/index.html.twig file to add this code in the {% block body %} ... {% endblock %}:

{% if error %}
    <div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
{{ form_start(form, {'action': path('login_check'), 'method': 'POST'}) }}
    {{ form_widget(form) }}
{{ form_end(form) }}

Database Generation

php bin/console doctrine:migrations:generate
php bin/console doctrine:migrations:migrate

This should have generated your database, according to your User and UserRole entities.

Generate Password

The following command will provide you with a hashed password you can directly insert into the database. The password will be hashed with the algorithm specified in security.yaml.

php bin/console security:encode-password my-password

Hope this helps!




回答2:


Thank you very much, the documentation lacks some important things, but this works very good. For others, check the order of the access-control entries, because one misplaced entry may block the whole process.

    access_control:
    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin, roles: ROLE_ADMIN }

This was working, but this not

    access_control:
    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin, roles: ROLE_ADMIN }
    - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }


来源:https://stackoverflow.com/questions/50082673/symfony-4-login-form-with-security-and-database-users

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!