start-kit-symfony / start-bundle
Symfony StartBundle 带用户和安全功能
Requires
- php: >=7.0.0
- aws/aws-sdk-php: ^3.30
- doctrine/doctrine-bundle: ^1.8
- doctrine/doctrine-migrations-bundle: ^1.0
- doctrine/orm: ^2.5
- egulias/email-validator: ^2.1
- facebook/graph-sdk: ^5.5
- google/apiclient: ^2.0
- guzzlehttp/guzzle: ^6.3
- namshi/jose: ^7.2
- nelmio/api-doc-bundle: ~3.0@beta
- sensio/framework-extra-bundle: 3.0.*|^4.0
- symfony/asset: ^4.0 | ^3.4
- symfony/event-dispatcher: ~3.0|^4.0
- symfony/expression-language: ^4.0 | ^3.4
- symfony/form: ~3.0|^4.0
- symfony/framework-bundle: ~3.0|^4.0
- symfony/monolog-bundle: ^3.1
- symfony/security-bundle: 3.4.*|^4.0
- symfony/templating: ^4.0 | ^3.4
- symfony/twig-bridge: ~3.0|^4.0
- symfony/twig-bundle: ^4.0 | ^3.4
- symfony/validator: ~3.0|^4.0
- symfony/var-dumper: ^4.0 | ^3.4
- twig/twig: ^2.4
Requires (Dev)
- doctrine/doctrine-fixtures-bundle: ^2.3
- liip/functional-test-bundle: ^1.8
- mockery/mockery: ^0.9.9
- nelmio/alice: ^3.1
- phpunit/phpunit: ^6.2
- sensio/generator-bundle: ^3.0
- symfony/phpunit-bridge: ^3.0
This package is not auto-updated.
Last update: 2024-09-20 08:00:20 UTC
README
Symfony 4 安装指南
- 安装包,现在不要执行配方。
composer require start-kit-symfony/start-bundle
- 将 API 路由添加到 config -> routes.yaml 中的包
starter_kit_start:
resource: "@StarterKitStartBundle/Resources/config/routing.yml"
- 在 -> config 文件夹中的 bundles.php 中添加 NelmioApiDocBundle
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true],
- 在 config 文件夹中添加 nelmio_api_doc.yaml 并粘贴以下内容。
nelmio_api_doc:
routes:
path_patterns: # an array of regexps
- ^/(api(?!-docs))
- ^/oauth
- ^/login_check
- ^/access-tokens
models: { use_jms: false }
documentation:
info:
title: 'Symfony Starter Api'
description: 'Our Symfony Starter Kit Api Documentation.'
version: 1.0.0
- 将服务别名添加到 services.yaml 文件中。这是一个临时的解决方案。
Nelmio\ApiDocBundle\ApiDocGenerator: '@nelmio_api_doc.generator'
- 添加一个名为 ApiDocController 的控制器类,并添加一个名为 apiDoc 的方法。
<?php
namespace App\Controller;
use Nelmio\ApiDocBundle\ApiDocGenerator;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ApiDocController extends Controller
{
/**
* @var ApiDocGenerator
*/
private $apiDocGenerator;
public function __construct(ApiDocGenerator $apiDocGenerator)
{
$this->apiDocGenerator = $apiDocGenerator;
}
/**
* @Route(name="api_docs", path="api-docs", methods={"GET"})
*
* @param Request $request
* @return Response
*/
public function apiDoc(Request $request)
{
$spec = $this->apiDocGenerator->generate()->toArray();
if ('' !== $request->getBaseUrl()) {
$spec['basePath'] = $request->getBaseUrl();
}
return $this->render('@NelmioApiDoc/SwaggerUi/index.html.twig', ['swagger_data' => ['spec' => $spec]]);
}
}
- 在您的 var 文件夹中创建一个 jwt 目录
mkdir var/jwt
- 使用您使用的密码短语创建您的私钥并记下。
openssl genrsa -out var/jwt/private.pem -aes256 4096
- 创建您的公钥,您将需要在这里以及在 composer 安装步骤中使用密码短语。
openssl rsa -pubout -in var/jwt/private.pem -out var/jwt/public.pem
-
在您的 App -> Entity 文件夹中创建一个 User 类,该类继承自 BaseUser。
-
当您创建您的 s3 存储桶时,您需要为每个环境创建一个文件夹。在该文件夹中,您需要添加另一个名为 profile_pics 的文件夹,这是存储个人图片的地方。例如,您有 dev 和 prod。您可以覆盖此设置或选择不使用 s3。
prod -> profile_pics dev -> profile_pics
-
在 .env 文件中填写所有设置服务参数的信息。
###> start-kit-symfony/start-bundle ###
# The Secret Pass
JWS_PASS_PHRASE=secret_change
JWS_TTL=5184000
REFRESH_TOKEN_TTL=10368000
# Facebook Config
FACEBOOK_APP_SECRET=facebook_secret
FACEBOOK_APP_ID=facebook_app_id
FACEBOOK_API_VERSION=2.10
# Google
GOOGLE_CLIENT_ID=google_client_id
# Amazon
AWS_KEY=amazon_key
AWS_SECRET=amazon_secret
AWS_REGION=us-west-2
AWS_BUCKET=fake-bucket
AWS_VERSION=2006-03-01
# Slack
SLACK_CLIENT_KEY=slack_client_key
SLACK_CLIENT_ID=slack_client_id
###< start-kit-symfony/start-bundle ###
APP_EMAIL=fake_email@gmail.com
- 在 App 中创建一个名为 "Entity" 的文件夹,并在其中创建一个名为 User 的实体类。
<?php
namespace AppBundle\Entity;
use StarterKit\StartBundle\Entity\BaseUser;
use Doctrine\ORM\Mapping as ORM;
use StarterKit\StartBundle\Entity\FacebookTrait;
use StarterKit\StartBundle\Entity\GoogleTrait;
use StarterKit\StartBundle\Entity\ImageTrait;
use StarterKit\StartBundle\Entity\RefreshTokenTrait;
use StarterKit\StartBundle\Entity\SlackTrait;
/**
* @ORM\Entity(repositoryClass="StarterKit\StartBundle\Repository\UserRepository")
* @ORM\Table(name="User")
* @ORM\HasLifecycleCallbacks()
* @ORM\Table(name="User", indexes={
* @ORM\Index(name="idk_email", columns={"email"}),
* @ORM\Index(name="idk_google_user_id", columns={"google_user_id"}),
* @ORM\Index(name="idk_slack_user_Id", columns={"slack_user_id"}),
* @ORM\Index(name="idk_facebook_user_id", columns={"facebook_user_id"}),
* @ORM\Index(name="idk_forget_password_token", columns={"forget_password_token"}),
* @ORM\Index(name="idk_refresh_token", columns={"refresh_token"})
* })
* Class User
* @package AppBundle\Entity
*/
class User extends BaseUser
{
use ImageTrait;
use GoogleTrait;
use FacebookTrait;
use SlackTrait;
use RefreshTokenTrait;
}
- 注册防火墙和安全提供者配置 -> packages -> security.yaml for symfony 4。
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
cost: 12
# https://symfony.com.cn/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
email:
id: StarterKit\StartBundle\Security\Provider\EmailProviderInterface
slack:
id: StarterKit\StartBundle\Security\Provider\SlackProviderInterface
token:
id: StarterKit\StartBundle\Security\Provider\TokenProviderInterface
facebook:
id: StarterKit\StartBundle\Security\Provider\FacebookProviderInterface
google:
id: StarterKit\StartBundle\Security\Provider\GoogleProviderInterface
refresh:
id: StarterKit\StartBundle\Security\Provider\RefreshTokenProviderInterface
role_hierarchy:
ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
facebook:
pattern: ^/access-tokens/facebook
stateless: true
provider: facebook
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
google:
pattern: ^/access-tokens/google
stateless: true
provider: google
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
slack:
pattern: ^/oauth/slack*
stateless: true
provider: slack
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\OAuthGuardInterface
refresh:
pattern: ^/access-tokens/refresh
stateless: true
provider: refresh
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
login:
pattern: ^/login_check
stateless: true
provider: email
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
api:
pattern: ^/api*
anonymous: ~
stateless: true
provider: token
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\StateLess\ApiGuardInterface
main:
pattern: ^/*
anonymous: ~
provider: token
stateless: true
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\StateLess\WebsiteGuardInterface
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
Symfony 3 安装指南
- 安装包
composer require start-kit-symfony/start-bundle
- 将 Bundle 类添加到 app kernel。
new StarterKit\StartBundle\StarterKitStartBundle(),
-
切换到您的项目目录
-
在您的 var 文件夹中创建一个 jwt 目录
mkdir var/jwt
- 使用您使用的密码短语创建您的私钥并记下。
openssl genrsa -out var/jwt/private.pem -aes256 4096
- 创建您的公钥,您将需要在这里以及在 composer 安装步骤中使用密码短语。
openssl rsa -pubout -in var/jwt/private.pem -out var/jwt/public.pem
-
在您的 AppBundle -> Entity 文件夹中创建一个 User 类,该类继承自 BaseUser。
-
当您创建您的 s3 存储桶时,您需要为每个环境创建一个文件夹。在该文件夹中,您需要添加另一个名为 profile_pics 的文件夹,这是存储个人图片的地方。例如,您有 dev 和 prod。您可以覆盖此设置或选择不使用 s3。
prod -> profile_pics dev -> profile_pics
-
在 App 中创建一个名为 "Entity" 的文件夹,并在其中创建一个名为 User 的实体类。
<?php
namespace AppBundle\Entity;
use StarterKit\StartBundle\Entity\BaseUser;
use Doctrine\ORM\Mapping as ORM;
use StarterKit\StartBundle\Entity\FacebookTrait;
use StarterKit\StartBundle\Entity\GoogleTrait;
use StarterKit\StartBundle\Entity\ImageTrait;
use StarterKit\StartBundle\Entity\RefreshTokenTrait;
use StarterKit\StartBundle\Entity\SlackTrait;
/**
* @ORM\Entity(repositoryClass="StarterKit\StartBundle\Repository\UserRepository")
* @ORM\Table(name="User")
* @ORM\HasLifecycleCallbacks()
* @ORM\Table(name="User", indexes={
* @ORM\Index(name="idk_email", columns={"email"}),
* @ORM\Index(name="idk_google_user_id", columns={"google_user_id"}),
* @ORM\Index(name="idk_slack_user_Id", columns={"slack_user_id"}),
* @ORM\Index(name="idk_facebook_user_id", columns={"facebook_user_id"}),
* @ORM\Index(name="idk_forget_password_token", columns={"forget_password_token"}),
* @ORM\Index(name="idk_refresh_token", columns={"refresh_token"})
* })
* Class User
* @package AppBundle\Entity
*/
class User extends BaseUser
{
use ImageTrait;
use GoogleTrait;
use FacebookTrait;
use SlackTrait;
use RefreshTokenTrait;
}
- 在 app -> config -> config.yml 文件中配置 Bundle。
starter_kit_start:
login_url: '%app.login_url%' # this is the path that your login screen is. This where website guard will nagivate people if login is required and the user is not logged in.
jws_ttl: '%app.jws_ttl%' # This the number of seconds the jwt token will live
jws_pass_phrase: '%app.jws_pass_phrase%' # This the pass phrased you used to create jwt private / public keys.
refresh_token_ttl: '%app.refresh_token_ttl%' # This how long the refresh token will live.
user_class: '%app.user_class%' # This is concrete class that extends the base user
facebook_app_secret: '%app.facebook_app_secret%' # This is client secret that you get when you register your website with facebook
facebook_api_version: '%app.facebook_api_version%' # Facebook Api Version
facebook_app_id: '%app.facebook_app_id%' # This is your facebook app id
google_client_id: '%app.google_client_id%' # This is your google client id
# All this information is found when you create the bucket
aws_api_version: '%app.aws_api_version%'
aws_key: '%app.aws_key%'
aws_secret: '%app.aws_secret%'
aws_region: '%app.aws_region%'
aws_s3_bucket_name: '%app.aws_region%'
# This client secret / client are found when u register your app with slack
slack_client_secret: '%app.slack_client_secret%'
slack_client_id: '%app.slack_client_id%'
- 注册防火墙和安全提供者。这将在 app -> config -> security.yml 中。
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
cost: 12
# https://symfony.com.cn/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
email:
id: StarterKit\StartBundle\Security\Provider\EmailProviderInterface
slack:
id: StarterKit\StartBundle\Security\Provider\SlackProviderInterface
token:
id: StarterKit\StartBundle\Security\Provider\TokenProviderInterface
facebook:
id: StarterKit\StartBundle\Security\Provider\FacebookProviderInterface
google:
id: StarterKit\StartBundle\Security\Provider\GoogleProviderInterface
refresh:
id: StarterKit\StartBundle\Security\Provider\RefreshTokenProviderInterface
role_hierarchy:
ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
facebook:
pattern: ^/access-tokens/facebook
stateless: true
provider: facebook
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
google:
pattern: ^/access-tokens/google
stateless: true
provider: google
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
slack:
pattern: ^/oauth/slack*
stateless: true
provider: slack
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\OAuthGuardInterface
refresh:
pattern: ^/access-tokens/refresh
stateless: true
provider: refresh
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
login:
pattern: ^/login_check
stateless: true
provider: email
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\LoginGuardInterface
api:
pattern: ^/api*
anonymous: ~
stateless: true
provider: token
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\StateLess\ApiGuardInterface
main:
pattern: ^/*
anonymous: ~
provider: token
stateless: true
guard:
authenticators:
- StarterKit\StartBundle\Security\Guard\StateLess\WebsiteGuardInterface
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
项目概述
服务和接口
每个服务都有一个接口,该接口注册为服务。此包仅在类构造函数中使用接口。这意味着要覆盖服务,您只需找到它所实现的接口,并在应用程序包中注册该接口。
以下是一个示例。假设您想使用存储在数据库中的令牌而不是 jwt / jws 令牌。您只需创建一个实现了 AuthTokenServiceInterface 的服务并注册到应用程序包中即可。
服务注册
AppBundle\Service\DatabaseTokenService:
class: AppBundle\Service\DatabaseTokenService
arguments:
$ttl: '%app.jws_ttl%'
StarterKit\StartBundle\Service\AuthTokenServiceInterface: '@AppBundle\Service\DatabaseTokenService'
您可以在 这里 找到实际的类实现。
这是为包注册服务的位置。 services.yml
不使用 JMS Serializer,Symfony Serializer,FOS Rest Bundles
以下是决定不使用这些的原因。
- 使用序列化器比输出数组慢
- 使用数组和将它们放入JsonResponse中,可以更轻松地进行测试和单元测试。
- FOS Rest Bundle配置起来比较复杂,大多数项目都会使用json而不是xml,所以你可以根据这一点来偏置你的API。
- 如果你想添加这些,我也认为这些包的作者们做得非常出色。 ;)
无状态认证
我认为PHP会话比较复杂,版本之间差异很大。如果每个请求都有一个表示用户身份的令牌/字符串,那么理解认证会更简单。我相信这也有助于分离关注点,因为客户端负责存储认证令牌,服务器负责验证它。
Ajax登录
我认为使用Ajax登录并只让请求包含客户端存储的用于认证的cookie会更好。这意味着你不需要担心获取最后一个用户名和刷新页面。这也使得保护逻辑更简单,因为每个登录响应都将有一个认证cookie和认证响应。
响应封装
我认为每个响应都应该被封装在一个描述如何解析它的封装器中。这个项目使用的响应字段是元数据和数据。元数据将有一个类型,这将帮助客户端根据这些类型构建解析器。
{
"meta":...,
"data":...
}
仅电子邮件登录
我认为电子邮件是登录的最佳方法,而不是用户名。主要原因是它们对于网站来说是唯一的,如果一家网站收购了另一家网站,可以合并。
目录
如何/示例
如何运行测试
确保你的系统中安装了sqlite。这是我们用于测试数据库的。
-
git clone https://github.com/phptuts/StarterBundleForSymfony.git
-
进入你克隆仓库的目录。
-
在命令行中运行这个命令。
sh run_tests.sh