用户认证与授权:在 Symfony 中实现安全控制

news2024/9/9 0:30:57

用户认证与授权:在 Symfony 中实现安全控制

Symfony 是一个功能强大且高度可扩展的 PHP 框架,广泛用于开发复杂的 Web 应用程序。在现代 Web 应用程序中,用户认证和授权是不可或缺的功能,用于确保应用程序的安全性和数据的隐私性。本文将详细介绍如何在 Symfony 中实现用户认证和授权,具体到源码示例,帮助您全面掌握这一重要技能。

一、项目初始化

首先,我们需要创建一个 Symfony 项目。如果您还没有安装 Symfony,可以使用以下命令进行安装:

composer create-project symfony/skeleton my_project
cd my_project

接下来,安装用于用户认证和授权的必要包:

composer require symfony/security-bundle
composer require symfony/orm-pack
composer require symfony/maker-bundle --dev

二、创建用户实体

在 Symfony 中,用户实体通常用来表示应用程序中的用户。我们可以使用 maker-bundle 快速生成用户实体:

php bin/console make:user

按照提示输入必要的信息,例如用户类名 User,是否需要存储密码等。生成的用户实体类如下:

<?php

namespace App\Entity;

use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;

#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    #[ORM\Column(type: 'string', length: 180, unique: true)]
    private $email;

    #[ORM\Column(type: 'json')]
    private $roles = [];

    #[ORM\Column(type: 'string')]
    private $password;

    public function getId(): ?int
    {
        return $id;
    }

    public function getEmail(): ?string
    {
        return $email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUserIdentifier(): string
    {
        return (string) $this->email;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see PasswordAuthenticatedUserInterface
     */
    public function getPassword(): string
    {
        return $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }
}

三、数据库配置与迁移

接下来,我们需要配置数据库连接并进行迁移。首先,在 .env 文件中配置数据库连接信息:

DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name"

然后,运行以下命令以创建数据库和迁移用户实体:

php bin/console doctrine:database:create
php bin/console make:migration
php bin/console doctrine:migrations:migrate

四、实现用户注册功能

用户注册功能允许新用户在应用程序中创建账户。首先,我们需要创建一个注册表单。使用以下命令生成表单:

php bin/console make:registration-form

按照提示选择生成表单所需的选项。生成的表单类如下:

<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Length;

class RegistrationFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('email', EmailType::class)
            ->add('plainPassword', RepeatedType::class, [
                'type' => PasswordType::class,
                'first_options' => ['label' => 'Password'],
                'second_options' => ['label' => 'Repeat Password'],
                'invalid_message' => 'The password fields must match.',
                'options' => ['attr' => ['class' => 'password-field']],
                'required' => true,
                'mapped' => false,
                'constraints' => [
                    new NotBlank([
                        'message' => 'Please enter a password',
                    ]),
                    new Length([
                        'min' => 6,
                        'minMessage' => 'Your password should be at least {{ limit }} characters',
                        'max' => 4096,
                    ]),
                ],
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}

接下来,创建一个控制器来处理用户注册请求:

<?php

namespace App\Controller;

use App\Entity\User;
use App\Form\RegistrationFormType;
use App\Security\LoginFormAuthenticator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;

class RegistrationController extends AbstractController
{
    #[Route('/register', name: 'app_register')]
    public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher, UserAuthenticatorInterface $userAuthenticator, LoginFormAuthenticator $authenticator, EntityManagerInterface $entityManager): Response
    {
        $user = new User();
        $form = $this->createForm(RegistrationFormType::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // encode the plain password
            $user->setPassword(
                $userPasswordHasher->hashPassword(
                    $user,
                    $form->get('plainPassword')->getData()
                )
            );

            $entityManager->persist($user);
            $entityManager->flush();

            // do anything else you need here, like send an email

            return $userAuthenticator->authenticateUser(
                $user,
                $authenticator,
                $request
            );
        }

        return $this->render('registration/register.html.twig', [
            'registrationForm' => $form->createView(),
        ]);
    }
}

创建注册页面模板 templates/registration/register.html.twig

{% extends 'base.html.twig' %}

{% block body %}
<h1>Register</h1>

{{ form_start(registrationForm) }}
    {{ form_widget(registrationForm) }}
    <button type="submit" class="btn">Register</button>
{{ form_end(registrationForm) }}
{% endblock %}

五、实现用户登录功能

用户登录功能允许已注册用户访问受保护的资源。首先,使用以下命令生成登录表单:

php bin/console make:auth

按照提示选择 Login form authenticator 选项并提供必要的信息。生成的 LoginFormAuthenticator 类如下:

<?php

namespace App\Security;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private RouterInterface $router;

    public function __construct(RouterInterface $router)
    {
        $this->router = $router;
    }

    public function supports(Request $request): bool
    {
        return 'app_login' === $request->attributes->get('_route') && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
        ];
        $request->getSession()->set(Security::LAST_USERNAME, $credentials['email']);

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider): ?UserInterface
    {
        return $userProvider->load

UserByUsername($credentials['email']);
    }

    public function checkCredentials($credentials, UserInterface $user): bool
    {
        // Check the user's password, return true if it's valid
        // For example:
        // return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
        return true;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?RedirectResponse
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

        return new RedirectResponse($this->router->generate('homepage'));
    }

    protected function getLoginUrl(): string
    {
        return $this->router->generate('app_login');
    }
}

接下来,创建一个控制器来处理登录请求:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class SecurityController extends AbstractController
{
    #[Route('/login', name: 'app_login')]
    public function login(AuthenticationUtils $authenticationUtils): Response
    {
        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
    }

    #[Route('/logout', name: 'app_logout')]
    public function logout(): void
    {
        throw new \Exception('This method can be blank - it will be intercepted by the logout key on your firewall');
    }
}

创建登录页面模板 templates/security/login.html.twig

{% extends 'base.html.twig' %}

{% block body %}
<h1>Login</h1>

<form action="{{ path('app_login') }}" method="post">
    <label for="email">Email:</label>
    <input type="text" id="email" name="_username" value="{{ last_username }}" required autofocus>

    <label for="password">Password:</label>
    <input type="password" id="password" name="_password" required>

    <button type="submit">Login</button>

    {% if error %}
        <div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}
</form>
{% endblock %}

六、配置安全策略

为了保护应用程序的某些部分,我们需要配置安全策略。在 config/packages/security.yaml 文件中进行以下配置:

security:
    encoders:
        App\Entity\User:
            algorithm: auto

    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: true
            lazy: true
            provider: app_user_provider

            form_login:
                login_path: app_login
                check_path: app_login
                csrf_token_generator: security.csrf.token_manager

            logout:
                path: app_logout
                target: /

    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: ROLE_USER }

七、实现角色授权

在许多应用程序中,用户的权限根据其角色而不同。我们可以在用户实体中定义角色,并在控制器中根据角色进行授权。例如:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;

class AdminController extends AbstractController
{
    #[Route('/admin', name: 'admin')]
    #[IsGranted('ROLE_ADMIN')]
    public function index(): Response
    {
        return new Response('Admin area');
    }
}

在上述示例中,只有具有 ROLE_ADMIN 角色的用户才能访问 /admin 路由。

八、保护特定资源

您可以使用注释来保护特定的控制器方法。例如:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;

class AccountController extends AbstractController
{
    #[Route('/account', name: 'account')]
    #[IsGranted('ROLE_USER')]
    public function index(): Response
    {
        return new Response('Account area');
    }
}

在上述示例中,只有具有 ROLE_USER 角色的用户才能访问 /account 路由。

九、总结

本文详细介绍了在 Symfony 中实现用户认证与授权的全过程。从项目初始化、创建用户实体、配置数据库、实现用户注册和登录功能,到配置安全策略和实现角色授权,涵盖了每个步骤的具体代码和配置示例。通过这些示例,您可以全面掌握在 Symfony 中实现安全控制的技巧,为构建安全的 Web 应用程序奠定坚实的基础。

希望本文能对您有所帮助,如果您有任何问题或需要进一步的帮助,请随时与我联系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1958535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【java】 力扣 最后一个单词的长度

目录 题目链接题目描述代码 题目链接 58.最后一个单词的长度 题目描述 代码 public int lengthOfLastWord(String s) {int n s.length();int count 0;if(s null ||s.length() 0){return 0;}for(int i n-1;i>0;i--){if(s.charAt(i) ){if(count 0) continue;break;}count…

在pytroch中使用CIFAR10完成完整的模型训练套路

模型训练套路&#xff1a; 1.准备数据集2.加载数据集3.搭建神经网络4创建损失函数5.优化器6.设置训练网络的一些参数7.添加tensorboard&#xff08;方便观察&#xff09;8.开始训练.测试9.保存神经网络 准备数据 #准备数据集 dataset_traintorchvision.datasets.CIFAR10("…

外星人入侵_外星人

项目_外星人入侵_外星人 1创建第一个外星人1.1创建Alien类1.2创建Alien实例1.3让外星人出现在屏幕上 2创建一群外星人2.1确定一行可以容纳多少外星人2.2 创建多行外星人2.3创建外星人群2.4重构create_fleet()2.5添加行 3让外星人群移动3.1向右移动外星人3.2创建表示外星人移动方…

迷你世界魔方模型快速制作

做六个不一样颜色的顶部 --黄&#xff0c;绿&#xff0c;红&#xff0c;蓝&#xff0c;橙&#xff0c;白 --local ids{4000,3999, 3998,3997,3996,3995} 游戏脚本运行上一期文章 local x0,y0,z0-39,7,10--起点坐标 --框架、底面、侧面1-4、顶面 local id{682,671,681,680,66…

消息队列rabbitmq的使用

前提条件&#xff1a;环境安装amqp和安装rabbitmq sudo apt-get update sudo apt-get install rabbitmq-amqp-dev 1、创建CMakeLists.txt文件 # Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved.# CMake lowest version requirement cmake_minimum_…

tof系统标定流程之lens标定

1、lens标定详解 为什么在标定tof时需要进行lens的标定,可以说lens标定是一个必不可少的步骤,tof模组也是有镜头的,镜头的畸变会导致进入的光线出现偏差,最终照射到tof芯片表面导致深度图的分布出现畸变,通常是枕形畸变。例外一个用途在于,在计算fppn误差环节需要知道镜头…

机器学习算法与Python实战 | 两行代码即可应用 40 个机器学习模型--lazypredict 库!

本文来源公众号“机器学习算法与Python实战”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;两行代码即可应用 40 个机器学习模型 今天和大家一起学习使用 lazypredict 库&#xff0c;我们可以用一行代码在我们的数据集上实现许多…

【数据结构】队列(链表实现 + 力扣 + 详解 + 数组实现循环队列 )

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…

流行巨星布兰妮·斯皮尔斯发生了什么事?她现在在哪里过得怎么样

流行音乐公主布兰妮斯皮尔斯是 21 世纪初的经典偶像。她从 15 岁起就开始唱歌和表演&#xff0c;并创作了《Oops I Did it Again》和《Baby One More Time》等热门歌曲。她的歌曲非常出色&#xff0c;在 2000 年荣登榜首。她接下来的几张专辑变得更加畅销&#xff0c;她毫不畏惧…

学习008-02-04-04 Enable Split Layout in a List View(在列表视图中启用拆分布局 )

Enable Split Layout in a List View&#xff08;在列表视图中启用拆分布局 &#xff09; This lesson explains how to enable a Split Layout in a List View. 本课介绍如何在列表视图中启用拆分布局。 The Detail View opens when you select an object from the List Vie…

G120 EPos配置方案及应用场景

EPos功能就是基本定位器功能,它可计算出轴的运行特性,使轴以时间最佳的方式移动到目标位置。EPos功能主要包括:设定值 直接给定(MDI)功能、 选择程序段功能、回参考点功能、点动功能、运行到固定挡块功能。 EPos功能通过处理给定的加速度、速度和位置值生成运行特性曲线,…

node+mysql+layui+ejs实现左侧导航栏菜单动态显示

nodemysqllayuiejs实现左侧导航菜单动态显示 实现思路效果图数据库技术栈代码实现main.html&#xff08;前端首页页面&#xff09;查询资源菜单方法 jsapp.js配置ejs模板 node入门到入土项目实战开始&#xff0c;前端篇项目适合node小白入门&#xff0c;因为我也是小白来学习no…

机器人笛卡尔空间阻抗控制

机器人笛卡尔空间阻抗控制是一种重要的机器人控制策略,它关注于机器人末端执行器在笛卡尔空间(即任务空间)内的动态特性,以实现与环境的柔顺交互。以下是对机器人笛卡尔空间阻抗控制的详细解释: 一、基本概念 笛卡尔空间:指机器人末端执行器(如手爪、工具等)所处的三维…

Hive之扩展函数(UDF)

Hive之扩展函数(UDF) 1、概念讲解 当所提供的函数无法解决遇到的问题时&#xff0c;我们通常会进行自定义函数&#xff0c;即&#xff1a;扩展函数。Hive的扩展函数可分为三种&#xff1a;UDF,UDTF,UDAF。 UDF&#xff1a;一进一出 UDTF&#xff1a;一进多出 UDAF&#xff1a…

YOLO v8目标检测(三)模型训练与正负样本匹配

YOLO v8目标检测 损失函数理论 在YOLO v5模型中&#xff0c;cls, reg, obj代表的是三个不同的预测组成部分&#xff0c;对应的损失函数如下&#xff1a; cls: 这代表类别预测&#xff08;classification&#xff09;。对应的损失是类别预测损失&#xff08;loss_cls&#xff…

Win10出现错误代码0x80004005 一键修复指南

对于 Windows 10 用户来说&#xff0c;错误代码 0x80004005 就是这样一种迷雾&#xff0c;它可能在不经意间出现&#xff0c;阻碍我们顺畅地使用电脑。这个错误通常与组件或元素的缺失有关&#xff0c;它可能源自注册表的错误、系统文件的损坏&#xff0c;或者是软件的不兼容。…

listener监听

背景: 过滤器代码也可实现接口请求次数统计,但会影响过滤器本意;故在dispatcher servlet层进行监听统计 价值: 所有接口的次数统计可适用于系统全天访问量; 单个请求接口的次数统计可在企业中根据接口次数的高低,可分析出接口对应的功能受用户的喜好程度 请求通过过滤器到了s…

common-intellisense:助力TinyVue 组件书写体验更丝滑

本文由体验技术团队Kagol原创~ 前两天&#xff0c;common-intellisense 开源项目的作者 Simon-He95 在 VueConf 2024 群里发了一个重磅消息&#xff1a; common-intellisense 支持 TinyVue 组件库啦&#xff01; common-intellisense 插件能够提供超级强大的智能提示功能&…

c生万物系列(职责链模式与if_else)

从处理器的角度来说&#xff0c;条件分支会导致指令流水线的中断&#xff0c;所以控制语句需要严格保存状态&#xff0c;因为处理器是很难直接进行逻辑判断的&#xff0c;有可能它会执行一段时间&#xff0c;发现出错后再返回&#xff0c;也有可能通过延时等手段完成控制流的正…

skynet 实操篇

文章目录 概述demo启动文件skynet_start配置文件main.luastart函数thread_workerskynet_context_message_dispatchskynet_mq_popdispatch_message 小结 概述 上一篇写完skynet入门篇&#xff0c;这一篇写点实操性质的。 demo 对于一个开源框架&#xff0c;大部分都有他们自己…