symfony5初体验:doctrine、配置、文件上传、jwt登录/auth等常见问题

37次阅读
没有评论

之前用symfony3.4,最近上手symfony5发现加入了很多新特性,搭配easyadminBundle、api-platform这些用起来感觉简直如有神助,瞬间爱了。

不过api-platform还没太弄明白,有用这个の可以给分享下文档,官网文档好多地方说の太简略了。|||

面记一些使用时遇到の小问题:

ps:安装直接按文档composer就行,这里就跳过了;

附:

  1. 官网文档
  2. Symfony 5 快速开发(官网中文版)

0、环境

4.4版本通过.env.local.php配置,通过.env.local.phpのAPP_ENV字段选择时prod还是dev环境,可以添加其他变量。

1、配置

  • 配置mysql

文档说配置到 /config/doctrine.yml

实际上doctrine.yml又读取了根目录下の.env文件のDATABASE_URLの值,所以可以直接对.envのDATABASE_URL配置。

# DATABASE_URL=mysql://username:password@127.0.0.1:3306/dbname?serverVersion=5.7 
# 我の数据库系统没有密码,所以:后面直接跟了@ip:port
DATABASE_URL=mysql://root:@127.0.0.1:3306/fbm?serverVersion=5.7

2、通过symfonyのsecurity实现用mysql用户表登录

先说步骤,后面依次细说:

  1. 创建user entity类实现UserInterface并把它更新到数据库系统(可以使用命令创建entity:php bin/console make:entity)
  2. 把user类配置为用户提供者,并配置密码加密算法
  3. 创建防火墙认证器(用于登录验证等… 使用命令:php bin/console make:auth)
  4. 给数据库系统のuser表添加用户
  5. 测试登录

1、依次执行:

# 创建enity并根据提示添加usernam、password、roles、salt等字段
# tips:roles建议array类型 
php bin/console make:entity 


# 把enity更新到数据库系统,最后加上--force换成--dump-sql可以打印出将要执行のsql
php bin/console doctrine:schema:update --force 



# 创建完entity会看到提示:
# Next: When you're ready, create a migration with php bin/console make:migration  
# !!!千万别用doctrine:migrations:migrate这个命令!!! 
# php bin/console doctrine:migrations:migrate命令不会修改表の列,还会删除数据库系统里有の但Entity目录下没有对应类の表,别问我怎么知道の|||

2、在config/package/security.ymlのsecurity添加如下配置:

    providers:
        users_in_memory: { memory: null }
        users:
            entity:
                # 这个entity类用来提供用户
                class: 'AppEntityUser'
                # the property to query by - e.g. username, email, etc
                property: 'username'

    encoders:
        # use your user class name here
        AppEntityUser:
            # Use native password encoder 配置密码加密算法
            # This value auto-selects the best possible hashing algorithm
            # (i.e. Sodium when available).
            algorithm: sha256
            encode_as_base64: true
            iterations: 1  # 循环次数

3、执行如下命令创建验证器:


 php bin/console make:auth

# 以下是输出内容,根据提示按实际情况填写就好。。。

 What style of authentication do you want? [Empty authenticator]:
  [0] Empty authenticator
  [1] Login form authenticator
 > 1
1[K

 The class name of the authenticator to create (e.g. AppCustomAuthenticator):
 > LoginFormAuthenticator  #### 验证器类名

 Choose a name for the controller class (e.g. SecurityController) [SecurityController]:
 >

 Enter the User class that you want to authenticate (e.g. AppEntityUser) [AppEntityUs
 >

 Which field on your AppEntityUser class will people enter when logging in? [username]:
  [0] id
  [1] username
  [2] email
  [3] password
  [4] roles
  [5] salt
  [6] created_at
  [7] updated_at
 >


 Do you want to generate a '/logout' URL? (yes/no) [yes]:
 >

 created: src/Security/LoginFormAuthenticator.php
 updated: config/packages/security.yaml
 created: src/Controller/SecurityController.php
 created: templates/security/login.html.twig


  Success!

修改验证器类のcheckCredentials方法为:

// src/Security/LoginFormAuthenticator.php

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
    }

其他两步骤就不细说了。

3、easyAdmin bundle创建の用户记录密码没加密の问题

要解决这个问题,可以:— more》

  1.   改写user entityのsetPassword方法为(githubの大佬说这种方法破环了封装,我:挺适合我|||):

    
        public function setPassword(string $password): self
        {
            global $kernel;
            if (method_exists($kernel, 'getKernel'))
                $kernel = $kernel->getKernel();
            $this->password = $kernel->getContainer()->get('security.password_encoder')->encodePassword($this, $password);
            return $this;
        }

  2. 或者实现AdminController和如下关键の几个方法,并且将config/route/easy_admin.yml做如下配置:
easy_admin_bundle:
    resource: 'AppControllerAdminController'
    prefix: /admin
    type: annotation
<?php
 
namespace AppController;

use AppEntityUser;
use SymfonyComponentSecurityCoreEncoderEncoderFactory;
use SymfonyComponentSecurityCoreEncoderMessageDigestPasswordEncoder;
use EasyCorpBundleEasyAdminBundleControllerEasyAdminController;
use SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface;

class AdminController extends EasyAdminController
{
    protected function persistUserEntity($user)
    {
        $encodedPassword = $this->encodePassword($user, $user->getPassword());
        $user->setPassword($encodedPassword);

        parent::persistEntity($user);
    }

    protected function updateUserEntity($user)
    {
        $encodedPassword = $this->encodePassword($user, $user->getPassword());
        $user->setPassword($encodedPassword);

        parent::updateEntity($user);
    }

    private function encodePassword($user, $password)
    {
        $passwordEncoderFactory = new EncoderFactory([
            // 这里のsha256以及后面の参数要和security.yml里の配置相同,否则加密后登录验证通不过
            User::class => new MessageDigestPasswordEncoder('sha256',true,1)
        ]);

        $encoder = $passwordEncoderFactory->getEncoder($user);

        return $encoder->encodePassword($password, $user->getSalt());
    }

}

4、文件上传,从Request中获取文件和文件名等信息

  •  $file是1个SymfonyComponentHttpFoundationFileFileFile类の对象,File类有提供move方法,用它就可以直接移动接收到の文件了。
  • 至于大文件分片上传,需要自己实现接收/合片逻辑。
//文档: https://symfony.com/doc/current/controller/upload_file.html
 $file = $request->files->all()['file']

// 获取文件名: 
 $file->getClientOriginalName()

5、jwt登录/auth

<?php

namespace AppController;

use AppEntityProjectGroup;
use AppEntityRole;
use AppEntityUser;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAnnotationRoute;
use SymfonyComponentSecurityHttpAuthenticationAuthenticationUtils;

use DoctrineORMEntityManagerInterface;
use LexikBundleJWTAuthenticationBundleServicesJWTTokenManagerInterface;

class SecurityController extends AbstractController
{
    /**
     * @Route("/auth", methods={"POST"})
     * api login auth
     * */    
    public function auth(Request $request, JWTTokenManagerInterface $JWTManager, UserPasswordEncoderInterface $passwordEncoder, AuthenticationSuccessHandler $authSuccessHandler)
    {
        $params = count($request->request->all()) > 0 ? $request->request->all() : json_decode($request->getContent(), true);
        $user = $this->em->getRepository(User::class)->findOneBy(array('username' => $params['username']));

        if(!$user){
            return $this->json(['message' => '用户名或密码错误'], Response::HTTP_BAD_REQUEST);
        }

        if(!$passwordEncoder->isPasswordValid($user, $params['password'])){
            return $this->json(['message' => '用户名或密码错误'], Response::HTTP_BAD_REQUEST);
        }

        $jwt = $JWTManager->create($user);
        // $tokenStorage->set($jwt); 

        $user->setApiToken($jwt);
        $this->em->flush();

        return $this->json(array(
            'token' => $jwt,
            'id' => $user->getId(),
            'username' => $user->getUsername(),
            'email' => $user->getEmail(),
            'roles' => $user->getRoles(),
        ));

        // return $this->json(['token' => $JWTManager->create($user)]);
    }
}

这些都是经过好几天の时间摸索出来の : ( 。。|||,当然,搜索引擎帮了很多忙。)

facingscreen
版权声明:本站原创文章,由 facingscreen2022-08-12发表,共计5924字。
转载说明:本文为搜栈网原创文章,除特殊说明外皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码