Symfony 5.2 Secutrity 之 Auth Form Login 的流程

AuthLog属于securtiy模块,配置文件是security.yaml文件。配置中主要的key是 firewall.

负责Form登陆鉴定的类主要是LoginFormAuthenticator , 实现了 AuthenticatorInterface .

关于几个概念, 可以参考这篇文章

  • authen-tication 鉴权 (who are you?)
  • autho-rization 授权 (what permission do you have?)

AbstractFormLoginAuthenticator (以下简称AFA)

  • AFA::supports($request $req) bool
    Does the authenticator support the given Request? If this returns false, the authenticator will be skipped.
    用来判断是否需要使用登录鉴定
  • AFA::getCredentials(Request $req)::array
    用来获取客户输入的登录凭证(用户名和密码),同时把用户的最后一次输入的用户名保存到session中,已备现实错误信息时使用
  • AFA::getUser($credentials, $userProvider): userInterface
    从userProvider获取用户模型。如果用户没有找到呢?
  • AFA::checkCredentials(credentials, user): bool
    比较credentials中的输入密码和user中保存的密码
  • AFA::onAuthenticationSuccess()
    登陆成后的回调函数
  • AFA::onAuthenticationFailure($request, $exception)
    登陆失败的回调函数。用于比如记录异常的信息到session。
  • AFA::getLoginUrl(): string
    用于但登陆失败,需要把用户重新引到到登陆页面地址

备注: 完成文章后发现Symfony 5.3对Auth做了改动。所以请注意版本。

How to install and uninstall Elasticsearch on Mac

How to install:

brew tap elastic/tap
brew install elastic/tap/elasticsearch-full

From:

https://www.elastic.co/guide/en/elasticsearch/reference/current/brew.html

How to uninstall

#!/usr/bin/env sh

# checks to see if running 
launchctl list | grep elasticsearch
 
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist
launchctl remove homebrew.mxcl.elasticsearch

pkill -f elasticsearch

rm -f ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist

brew uninstall elasticsearch

# double check existence
ls -al /usr/local/bin/elasticsearch*
ls -al ~/Library/LaunchAgents

From:

https://gist.github.com/jkubacki/e2dd904bd648b0bd4554

PHP的运行模式:Apache 2.0 Handler 到 FPM

长久以来,我们一直使用Apache作为Web服务器,用Apache2.0 Handler 方式来运行PHP, 也就是mod_php, 把PHP中作为Apache的一个module的来运行。当时算是比较快的配置方式。不过这都是10多年前的配置了。

现在常用的搭配是nginx + PHP-FPM. 这种配置在目前docker时代显得比较合适。web server选用nginx 运行在一个容器内. php 作为一个单独的容器运行,通常打开9000端口来和nginx通讯。

在PHP端的dockerfile配置

选择 php:fpm-alpine 的镜像。记得最后要expose 9000端口。

在 Nginx 段的配置

/etc/nginx/cond.d/default.conf

upstream php-upstream {
    server php:9000
}

这样配置,通过phpfinfo查看, Server API就是 FPM/FastCGI 了。

附录 1: php的dockerfile如下

# newest PHP version
FROM php:fpm-alpine

# Add git
RUN apk --update --no-cache add git

# PostgreSQL
RUN set -ex && apk --no-cache add postgresql-dev
RUN docker-php-ext-install pdo_mysql

COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN rm -rf /var/cache/apk/* && rm -rf /tmp/*

WORKDIR /var/www

CMD composer -V; php-fpm

EXPOSE 9000

注意,需要手动的启动 php-fpm

附录2:nginx的Dockerfile:

FROM nginx:alpine

WORKDIR /var/www

ADD nginx.conf /etc/nginx
ADD sites /etc/nginx/sites-available
ADD conf.d /etc/nginx/conf.d

CMD ["nginx"]

EXPOSE 80 443

附录3: Docker-compose.yml

version: '3'

services:

  php:
    build: ./docker/php
    image: base-php:latest
    volumes:
      - ./app:/var/www:cached

  nginx:
    build: ./docker/nginx
    image: base-nginx:latest
    volumes:
      - ./app:/var/www:cached
      - ./logs/nginx:/var/log/nginx:cached
    ports: [80:80, 443:443]

PHP在CGI模式下无法获取http auth的问题

昨天发现一个问题。添加了一个用php发出http auth的脚本,在本地是测试正常,上线后却反复弹出auth窗口,似乎无法通过验证。

分析后发现,本地php是用moudle方式运行的。在live是CGI/FastCGI 。 查阅后得知,由于收到CGI协议的限制,无法使用类似 PHP_AUTH_*这样的超级变量。

解决办法是通过 Apache 的rewrite功能,手动地把http auth 请求加入到环境变量中。

.htaccess 大致如下

    #
    # For CGI/FastCGI , attach Authroziation header in Env Variabels.
    #
    RewriteCond %{HTTP:Authorization} ^(.*)
    RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]

这样,在php内就可以通过$_SERVER[‚HTTP_AUTHORZATION‘] 来获取http 的 Authorization的头部信息了。然偶通过字符串和base64解码就能获得用户输入的密码了。

学习笔记 ibexa Exp 教程

ibexa Exp教程2 是2阶课程。在1阶内已经完成了基本的安装,模板显示等初级教学。在本阶内主要学习以下内容:

  • 理解页面工作原理和架构
  • 能够自行创建页面和布局
  • 能够定制页面块
  • 能够创建自定义块
  • 理解表单构造和配置自定义表单
  • 理解样式化自定的模块

简介

按照在1阶学到的知识,首先是创建2个新的content type:

  • 狗品种页面
  • 建议贴士页面

任务1 创建入口页(Landing page)

后台Content sturcture里可以啊看到一个content tree. 这里用来组织你的内容结构。其中根内容就是站点的入口 Landing Page。 你可以创建子目录,在子目录内创建子内容。这会影响以后你页面上主菜单的显示(需要代码支持)。

任务2 使用后台site预览首页

后台site 站点是一个可视化的内容编辑器。它会根据你设定的页面布局定义(Layout)来显示初步的页面框架。

页面布局定义的key位于ezplatform_page_fieldtype.layouts下。配置内容是在yaml里定义的。以下范例内容定义了一个名为sidebar的页面布局layout。

ezplatform_page_fieldtype:
    layouts:
        sidebar:
            identifier: sidebar
            name: Right sidebar
            description: Main section with sidebar on the right
            thumbnail: /assets/images/layouts/sidebar.png
            template: layouts/sidebar.html.twig
            zones:
                first:
                    name: First zone
                second:
                    name: Second zone

注意观察模板文件sidebar.html.twig, 会发现其中与配置项zones的对应关系。就是zones[n]。

这个layout模板是如何与站点的base模板绑定的呢?就是在site里,在编辑模式下,右上角的…按钮“switch layout“来定义的。

创建入口配置

起始页面定义的key:ezplatform.system.site.content_view 。原始配置如下

ezplatform:
    system:
        site:
            content_view:
                full:
                    welcome_page:
                        template: "@ezdesign/full/welcome_page.html.twig"
                        match:
                            Id\Location: 2
                        params:
                            project_dir: '%kernel.project_dir%'

这个配置不再需要,可以删除。

在新的配置里被放到了 config/packages/view.yaml 中, 注意这个yaml文件名不包含ezplattform字样,很容易被忽略。)

Layout的key: ezpublish.system.site.page_layout

然后在 ezpublish.system.site.content_view.full下添加一个key: ‘landingg_page’, 内容是

landing_page:
    template: "full/landing_page.html.twig"
    match: 
        identifier\ContentType: "landing_page"

ezpublish.system.site.user.layout

具体任务

1 创建一个内建的“列表内容块” (content list block)

任务: 在首页landing page显示一个狗的种类列表

2 创建一个内建的”计划内容块“ (content scheduler block)

任务: 创建功能文章

先在fieldtype里定义一个叫做“schedule” 的块,然后拥有一个名字为“featrued“的view.

3 创建一个自定义的随机内容块“ (custom block)

随机显示特定目录下的内容。完成这个任务需要:

  • 创建配置 (以便在后台编辑面板中出现并使用配置)
  • 创建模板
  • 创建监听器 (需要代码)
  • 注册监听器为服务

4 创建 Form Block

作为一个form,你需要在后台的form编辑器内定义好所需的字段,模板和样式表。然后在首页编辑器内,把form拖入多在的位置。

通过 form 发送经来的信息则会保存在后台 form 的 submissions 之内。

完成以上4个任务后,本教程告一段落。

模板之间的层级关系

pagelayout.html.twig顶级layout. 定义了header, footer,menu和content预留位1
– layouts/sidebar.html.twig定义了左右两栏式的页面布局。被放置到content预留位内。2
– full/landing_page_html.twig继承于pagelayout.html.twig, 填充了conent预留位1.1views.yaml里ezpublish > system > site > content_view > full > landing_page 绑定

总结

本教程主要演示了如何创建布局,创建2种内建内容类型(列表内容和计划内容),创建自定义内容类型,创建用户表单。

Veröffentlicht unter 编程

Add JPEG support in GD under docker php7.4

一开始表现是在后台无法上传图片。安装xdebug后跟踪发现是validation错误。再进入imageValidator跟踪发现是校验图片的时候,需要从上传图片的字符串中重新创建图片。再进一步发现错误是“imagescreatefromstring(): No JPEG support in this PHP build“.

查看phpfino里的gd部分,果然没有JPEG。于是修改dockerfile. 在gd install之前加上一句

RUN docker-php-ext-configure gd --with-freetype --with-jpeg --enable-gd
RUN docker-php-ext-install gd

然后build一下,就好了。

参考

https://github.com/docker-library/php/issues/881#issuecomment-702578661

xdebug error message „Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9003“ in docker

I got this error message in docker log output:

Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9003

I could not connect xdebug in phpstorm. I checked port config, it is correct. But I am not sure, where the message „host.docker.internal“ come from. Until I found here:

https://github.com/docker/for-linux/issues/264#issuecomment-381437057

and

https://stackoverflow.com/questions/31324981/how-to-access-host-port-from-docker-container/43541732#43541732

So I add following config in my compose.yml for web server.

...
 extra_hosts:
      - "host.docker.internal:192.168.2.193"
...

After rebuild the image, you can check your hosts file in container. It has a new line in /etc/hosts, like

192.168.2.193 host.docker.internal

This is IP from my host, on that is runing phpstorm with listen to port 9003. And it works. Of course you must install xdebug and enbalbe it first. And check your php.ini the section:

[xdebug]
xdebug.mode=coverage
xdebug.client_host=localhost
xdebug.client_port=9003
xdebug.start_with_request=yes

Entity, Repository and Doctirne

三篇文章帮你理解Repository设计模式和运用.

我刚开始接触symfony的时候,觉得entity, repo类似zend framework里面的model和mapper的关系。 model是数据模型, mapper是操作数据模型。 显然这种理解是肤浅的。

Entity和Repo的概念是在DDD里面提出来的。详细的说明推荐阅读:殷浩详解DDD系列 第三讲 – Repository模式。 在这篇文章中,剖析Repo的本质比较透彻。他用的是Java世界的实现。在PHP世界,特别是Symfon框架内,与之相比又有一些比较特殊的地方。

Repo是一种规范,用来隔离Entity(实体,领域内的模型)和DO(数据模型,持久层的数据模型)。

1. Repo的使用“中性”的动词,而不该使用数据持久层的专用动词, 比如避免使用 select, insert, , update, delete这样的动词作为方法名。

2. Repo是服务于Entity的,同时自身不该直接操作DO(比如数据库)。 所以推荐动词findsaveremove 来作为方法名

在接触这些信息之前,我曾经在DH项目里自己实现过对DDD的理解。当时我理解的领域内的模型,就类似一个需要同时修改多个数据表的模型。

这篇文章中, 作者在symfony框架中如何使用Repo 发表了见解。 首先,他否定了symfony文档中的范例。在这个范例中,文档作者在controller里面通过继承获得的doctrie对象来获取entity manager, 然后用它创建Repo对象。作者说只有初学者才会在实际工作中使用这种方法,因为依赖被写死了。他建议使用service.

写一个repo service, 让controller只依赖这个service. 在这个service里,通过注入repo对象。然后repo对象也不要使用继承获得的findAll等方法,而只使用自家的方法。 作者称为“use composition instead of inheritance“. 所以需要写一个repo的interface,然后把自家的方法都在这个interface内定义好。这样写的好处是,脱耦。

结果调用关系就从

controller -> doctrine -> manager -> repo -> doctrine

变为了

controller-> service -> repo -> doctrine

这样的写的好处就是分层了。

在这篇文章内,不直接使用doctrine,而使用service,称为放弃 Service Locator Pattern ( 设计模式)而是使用 Dependcy injection (依赖注入模式)

不直接使用继承获得的方法,而是调用自己的方法叫做避免违反 Low of demeter 迪米特法则 (介绍)

分层好处的体现:

测试容易了。由于每一层只调用自己的下一层,所以mock起来特别方便。 要测试conterller只需要模拟一个service. 要测试service只需要模拟一个repo. 测试repo只需要模拟一个doctrine。避免了复杂的依赖关系

扩展容易了。比如如果需要在每次保存对象后触发一个事件,那么只要扩展service层就可以了。这样controller可以一直保持苗条。

Repository Pattern

Repository Pattern in Symfony

Doctrine Repositories should be collections without flush