月度存档: 1 月 2020

composer 安装完整教程(共4步)

安装前请务必确保已经正确安装了 PHP。

1.打开命令行窗口并执行 php -v 查看是否正确输出版本号。

2.打开命令行并依次执行下列命令安装最新版本的 Composer:

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

以上为局部安装,然后通过 php composer.phar 指令即可使用 Composer.

3.全局安装

全局安装是将 Composer 安装到系统环境变量 PATH 所包含的路径下面,然后就能够在命令行窗口中直接执行 composer 命令了。

sudo mv composer.phar /usr/local/bin/composer

4.更换中国镜像

禁用https源地址检查命令

composer config -g secure-http false

修改镜像源

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

查看全局的配置信息(是否修改成功)

composer config -g -l

如果只为某个项目局部换源

在项目的composer.json中添加如下代码:

{
"repositories": [
        {
            "type": "composer",
            "url": "https://mirrors.aliyun.com/composer/" //阿里云源
        },
        {
            "type": "composer",
            "url": "https://repo.packagist.org" //默认源
        },
}

ps:恢复默认源:
composer config -g --unset repos.packagist
如果不执行如上命令,会有如下错误提示如下所示
Installation failed, deleting ./composer.json.
File “./composer.json” cannot be found in the current directory

Express框架中处理GET和POST数据

如果直接用nodejs也可以,但是好麻烦,还是上框架简单:

var express        =         require("express");
var bodyParser     =         require("body-parser");
var app            =         express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.get('/',function(req,res){
  res.sendfile("index.html");
});
app.post('/',function(req,res){
  var data=req.body.data;
  res.end(data);
});
app.listen(8081,function(){
  console.log("Started on PORT 8081");
})

参考文章:

Handle GET and POST Request in Express


另外:我这还出现了throw er Unhandled error event的问题,是因为我上一次测试的端口还没有关,所以把占用的端口关掉就可以了
ps aux | grep node
kill port
或者:
ps aux | awk '/node/{print $2}' | xargs kill -9

centos crontab详解

1、crontab安装
centos最新版好像已经默认安装了

[root@CentOS ~]# yum install vixie-cron
[root@CentOS ~]# yum install crontabs
说明:
vixie-cron软件包是cron的主程序;
crontabs软件包是用来安装、卸装、或列举用来驱动 cron 守护进程的表格的程序。
cron 是linux的内置服务,但它不自动起来,可以用以下的方法启动、关闭这个服务:

/sbin/service crond start //启动服务
/sbin/service crond stop //关闭服务
/sbin/service crond restart //重启服务
/sbin/service crond reload //重新载入配置
查看crontab服务状态:service crond status

手动启动crontab服务:service crond start

2、crontab基本格式

复制代码

+—————- minute 分钟(0 – 59)

| +————- hour 小时(0 – 23)

| | +———- day 日期(1 – 31)

| | | +——- month 月份(1 – 12)

| | | | +—- week 星期(0 – 7) (星期天=0 or 7)

| | | | |

* * * * * 要运行的命令

复制代码
3、crontab命令编辑

crontab -u //设定某个用户的cron服务,一般root用户在执行这个命令的时候需要此参数
crontab -l //列出某个用户cron服务的详细内容   
crontab -r //删除没个用户的cron服务   
crontab -e //编辑某个用户的cron服务
(1)新建一个定时器,(普通用户的定时器,在普通用户下自己建)

crontab -e //先su切换到某个用户下,然后输入这个命令,然后进入编辑状态
然后输入定时器语句如下:(>>是把echo输出字符串打印到text.txt文件中)
0 6 * * * echo “Good morning.” >> /tmp/test.txt

(2)root查看自己的cron设置:

crontab -u root -l
(3)root想删除fred用户的cron设置:

crontab -u fred -r

4、常用示例

复制代码
每天早上6点追加一条字符串到一个文本。
0 6 * * * echo “Good morning.” >> /tmp/test.txt

每两个小时追加一条字符串一个文本。
0 */2 * * * echo “Have a break now.” >> /tmp/test.txt

晚上11点到早上8点之间每两个小时,早上八点
0 23-7/2,8 * * * echo “Have a good dream:)” >> /tmp/test.txt

每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点
0 11 4 * 1-3 command line

1月1日早上4点
0 4 1 1 * command line

每月每天每小时的第 0 分钟执行一次 /bin/ls
0 * * * * /bin/ls

在 12 月内, 每天的早上 6 点到 12 点中,每隔 20 分钟执行一次 /usr/bin/backup
*/20 6-12 * 12 * /usr/bin/backup

周一到周五每天下午 5:00 寄一封信给 alex_mail_name :
0 17 * * 1-5 mail -s “hi” alex_mail_name < /tmp/maildata

每月每天的午夜 0 点 20 分, 2 点 20 分, 4 点 20 分….执行 echo “haha”
20 0-23/2 * * * echo “haha”

晚上11点到早上8点之间每两个小时,早上8点,显示时间
0 23-7/2,8 * * * date
复制代码
每次编辑完某个用户的cron设置后, cron自动在/var/spool/cron下生成一个与此用户同名的文件,此用户的cron信息都记录在这个文件中,这个文件是不可以直接编辑的, 只可以用crontab -e 来编辑。cron启动后每过一份钟读一次这个文件,检查是否要执行里面的命令。因此此文件修改后不需要重新启动cron服务。

python定时任务

APScheduler

APScheduler(Python化的Cron)使用总结

简介

APScheduler全程为Advanced Python Scheduler,是一款轻量级的Python任务调度框架。它允许你像Cron那样安排定期执行的任务,并且支持Python函数或任意可调用的对象。官方文档:https://apscheduler.readthedocs.io/en/latest/userguide.html#basic-concepts

APScheduler安装

方法一:使用pip安装

$ pip install apscheduler
方法二:如果pip不起作用,可以从pypi上下载最新的源码包(https://pypi.python.org/pypi/APScheduler/)进行安装:

$ python setup.py install
APScheduler组件

triggers(触发器): 触发器中包含调度逻辑,每个作业都由自己的触发器来决定下次运行时间。除了他们自己初始配置意外,触发器完全是无状态的。

job stores(作业存储器):存储被调度的作业,默认的作业存储器只是简单地把作业保存在内存中,其他的作业存储器则是将作业保存在数据库中。当作业被保存到一个持久化的作业存储器中的时候,该作业的数据会被序列化,并在加载时被反序列化。作业存储器不能共享调度器。

executors(执行器):处理作业的运行,他们通常通过在作业中提交指定的可调用对象到一个线程或者进城池来进行。当作业完成时,执行器将会通知调度器。

schedulers(调度器):配置作业存储器和执行器可以在调度器中完成,例如添加、修改和移除作业。根据不同的应用场景可以选用不同的调度器,可选的有BlockingScheduler,BackgroundScheduler,AsyncIOScheduler,GeventScheduler,TornadoScheduler,TwistedScheduler,QtScheduler 7种。

调度器

BlockingScheduler : 当调度器是你应用中唯一要运行的东西时。
BackgroundScheduler : 当你没有运行任何其他框架并希望调度器在你应用的后台执行时使用(充电桩即使用此种方式)。
AsyncIOScheduler : 当你的程序使用了asyncio(一个异步框架)的时候使用。
GeventScheduler : 当你的程序使用了gevent(高性能的Python并发框架)的时候使用。
TornadoScheduler : 当你的程序基于Tornado(一个web框架)的时候使用。
TwistedScheduler : 当你的程序使用了Twisted(一个异步框架)的时候使用
QtScheduler : 如果你的应用是一个Qt应用的时候可以使用。
作业存储器

如果你的应用在每次启动的时候都会重新创建作业,那么使用默认的作业存储器(MemoryJobStore)即可,但是如果你需要在调度器重启或者应用程序奔溃的情况下任然保留作业,你应该根据你的应用环境来选择具体的作业存储器。例如:使用Mongo或者SQLAlchemy JobStore (用于支持大多数RDBMS)

执行器

对执行器的选择取决于你使用上面哪些框架,大多数情况下,使用默认的ThreadPoolExecutor已经能够满足需求。如果你的应用涉及到CPU密集型操作,你可以考虑使用ProcessPoolExecutor来使用更多的CPU核心。你也可以同时使用两者,将ProcessPoolExecutor作为第二执行器。

触发器

当你调度作业的时候,你需要为这个作业选择一个触发器,用来描述这个作业何时被触发,APScheduler有三种内置的触发器类型:

date 一次性指定日期
interval 在某个时间范围内间隔多长时间执行一次
cron 和Linux crontab格式兼容,最为强大
date

最基本的一种调度,作业只会执行一次。它的参数如下:

run_date (datetime|str) – 作业的运行日期或时间
timezone (datetime.tzinfo|str) – 指定时区
举个栗子:

2016-12-12运行一次job_function

sched.add_job(job_function, ‘date’, run_date=date(2016, 12, 12), args=[‘text’])

2016-12-12 12:00:00运行一次job_function

sched.add_job(job_function, ‘date’, run_date=datetime(2016, 12, 12, 12, 0, 0), args=[‘text’])
interval

间隔调度,参数如下:

weeks (int) – 间隔几周
days (int) – 间隔几天
hours (int) – 间隔几小时
minutes (int) – 间隔几分钟
seconds (int) – 间隔多少秒
start_date (datetime|str) – 开始日期
end_date (datetime|str) – 结束日期
timezone (datetime.tzinfo|str) – 时区
举个栗子:

每两个小时调一下job_function

sched.add_job(job_function, ‘interval’, hours=2)
cron

参数如下:

year (int|str) – 年,4位数字
month (int|str) – 月 (范围1-12)
day (int|str) – 日 (范围1-31)
week (int|str) – 周 (范围1-53)
day_of_week (int|str) – 周内第几天或者星期几 (范围0-6 或者 mon,tue,wed,thu,fri,sat,sun)
hour (int|str) – 时 (范围0-23)
minute (int|str) – 分 (范围0-59)
second (int|str) – 秒 (范围0-59)
start_date (datetime|str) – 最早开始日期(包含)
end_date (datetime|str) – 最晚结束时间(包含)
timezone (datetime.tzinfo|str) – 指定时区
举个栗子:

job_function将会在6,7,8,11,12月的第3个周五的1,2,3点运行

sched.add_job(job_function, ‘cron’, month=’6-8,11-12′, day=’3rd fri’, hour=’0-3′)

截止到2016-12-30 00:00:00,每周一到周五早上五点半运行job_function

sched.add_job(job_function, ‘cron’, day_of_week=’mon-fri’, hour=5, minute=30, end_date=’2016-12-31′)

配置调度程序

在应用程序中使用默认作业存储和默认执行程序运行BackgroundScheduler的例子:

from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()
# Initialize the rest of the application here, or before the scheduler initialization

这将生成一个名为“default”的MemoryJobStore和名为“default”的ThreadPoolExecutor的BackgroundScheduler,默认最大线程数为10。

如果不满足于当前配置,如希望使用两个执行器有两个作业存储器,并且还想要调整新作业的默认值并设置不同的时区,可按如下配置:

复制代码
from pytz import utc

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor

# 配置作业存储器
jobstores = {
‘mongo’: MongoDBJobStore(),
‘default’: SQLAlchemyJobStore(url=’sqlite:///jobs.sqlite’)
}

配置执行器,并设置线程数

executors = {
‘default’: ThreadPoolExecutor(20),
‘processpool’: ProcessPoolExecutor(5)
}
job_defaults = {
‘coalesce’: False, # 默认情况下关闭新的作业
‘max_instances’: 3 # 设置调度程序将同时运行的特定作业的最大实例数3
}
scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
复制代码

启动调度器

启动调度器只需要调用start()方法,对于除BlockingScheduler以外的调度程序,此调用将立即返回,您可以继续应用程序的初始化过程,可能会将作业添加到调度程序。

对于BlockingScheduler,只需在完成初始化步骤后调用start()

scheduler.start()

添加作业

方法一:调用add_job()方法

最常见的方法,add_job()方法返回一个apscheduler.job.Job实例,您可以稍后使用它来修改或删除该作业。

方法二:使用装饰器scheduled_job()

此方法主要是方便的声明在应用程序运行时不会改变的作业

删除作业

方法一:通过作业ID或别名调用remove_job()删除作业

方法二:通过add_job()返回的job实例调用remove()方法删除作业

举个栗子:

复制代码

实例删除

job = scheduler.add_job(myfunc, ‘interval’, minutes=2)
job.remove()
# id删除
scheduler.add_job(myfunc, ‘interval’, minutes=2, id=’my_job_id’)
scheduler.remove_job(‘my_job_id’)
复制代码

暂停和恢复作业

可以通过Job实例或调度程序本身轻松暂停和恢复作业。 当作业暂停时,下一个运行时间将被清除,直到作业恢复,不会再计算运行时间。 要暂停作业,请使用以下任一方法:

apscheduler.job.Job.pause()
apscheduler.schedulers.base.BaseScheduler.pause_job()

恢复作业:

apscheduler.job.Job.resume()
apscheduler.schedulers.base.BaseScheduler.resume_job()

获取作业列表

要获得计划作业的机器可处理列表,可以使用get_jobs()方法。 它将返回一个Job实例列表。 如果您只对特定作业存储中包含的作业感兴趣,则将作业存储别名作为第二个参数。

为了方便起见,可以使用print_jobs()方法,它将打印格式化的作业列表,触发器和下次运行时间。

修改作业属性

您可以通过调用apscheduler.job.Job.modify()或modify_job()来修改除id以外的任何作业属性。

job.modify(max_instances=6, name=’Alternate name’)

关闭调度程序

默认情况下,调度程序关闭其作业存储和执行程序,并等待所有当前正在执行的作业完成,wait=False参数可选,代表立即停止,不用等待。

scheduler.shutdown(wait=False)
附:1、定时任务运行脚本小例子:

复制代码
import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
from app.untils.log_builder import sys_logging

scheduler = BlockingScheduler() # 后台运行

# 设置为每日凌晨00:30:30时执行一次调度程序
@scheduler.scheduled_job(“cron”, day_of_week=’*’, hour=’1′, minute=’30’, second=’30’)
def rebate():
print “schedule execute”
sys_logging.debug(“statistic scheduler execute success” + datetime.datetime.now().strftime(“%Y-%m-%d %H:%M:%S”))

if name == ‘main‘:
try:
scheduler.start()
sys_logging.debug(“statistic scheduler start success”)
except (KeyboardInterrupt, SystemExit):
scheduler.shutdown()
sys_logging.debug(“statistic scheduler start-up fail”)
复制代码

Debian服务器之安装Python3.7

Debian服务器之安装Python3.7

1、系统环境介绍

1.1 系统版本

    debian-9.6.0-amd64-netinst

1.2 系统内核

    Linux lnnkee 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64 GNU/Linux

2、下载安装包

https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tar.xz
3、配置安装环境

apt install libffi-dev #否则将会提示No module named‘_ctypes’
4、编译安装python

tar xvf Python-3.7.0.tar
cd Python-3.7.0
./configure –prefix=/usr/local/python3.7/ –enable-optimizations
make && make install
5、设置默认python

ln -s /usr/local/python3.7/bin/python3.7 /usr/bin/python

ln -s /usr/local/python3.7/bin/pip3.7 /usr/bin/pip
6、安装完成

JQuery动态添加/删除class样式

有时我们会对某个控件的class样式进行获取、添加、修改、删除。下面是简单整理的一些实例用法:

html样例代码如下:

<ul class="pro_list fl cont">
    <li class="core"></li>
    <li class="core"></li>
    <li class="core"></li>
</ul>

1.获取样式名
var class_name=$(".pro_list").attr("class");
结果:”pro_list fl cont”

2.添加样式(给第一个li添加样式”current”,这里的添加是追加)

$(".pro_list li").eq(0).addClass("current");
执行后第一个li就变成如下:
<li class="core current"></li>
3.修改样式,其实就是将class属性重新赋值
$("ul").attr("class", "mylist");
执行结果:

<ul class="mylist">
    <li class="core"></li>
    <li class="core"></li>
    <li class="core"></li>
</ul>

4.删除样式
1)删除某个样式

$("ul").removeClass("fl");
执行结果:

<ul class="pro_list cont">
    <li class="core"></li>
    <li class="core"></li>
    <li class="core"></li>
</ul>

2)删除两个样式
方法一:
$("ul").removeClass("fl").removeClass("cont");
方法二:
$("ul").removeClass("fl cont");
执行结果:

<ul class="pro_list cont">
    <li class="core"></li>
    <li class="core"></li>
    <li class="core"></li>
</ul>

3)移除全部样式

$("ul").removeClass();

5.判断是否含有某个样式
方法一:

if($("ul").hasClass("cont")){
    alert(true);
}
else{
    alert(false);
}

方法二:

if($("ul").is(".cont")){
    alert(true);
}
else{
    alert(false);
}

6.样式名显示隐藏切换显示

$("ul li").toggleClass("current");

基于yii2的blog系统开发11-创建自定义小部件

接上一节:基于yii2的blog系统开发10-控制台命令程序的创建和执行:

第十八步:创建自定义小部件

1.在frontend/目录下新建文件夹components,在该文件夹下新建HelloWidget.php文件:

<?php
namespace app\components;
use yii\base\Widget;
use yii\helpers\Html;

class HelloWidget extends Widget
{
    public $mes;
    public function init()
    {
        parent::init();
        if($this->mes==null)
        {
            $this->mes = "hello";
        }
    }

    public function run()
    {
        return Html::encode($this->mes);
    }
}

2.在视图页面引用:

use frontend\components\HelloWidget;
<?= HelloWidget::widget(['mes'=>'hi,lq'])?>

基于yii2的blog系统开发10-控制台命令程序的创建和执行

接上一篇:基于yii2的blog系统开发9-授权ACF&RBAC:

第十七步 控制台命令程序的创建和执行

1.在console/controllers/目录下新建HelloController.php:

<?php
namespace console\controllers;
use Yii;
use yii\console\Controller;
class HelloController extends Controller
{
    public function actionIndex()
    {
       echo "hello console!\n";
    }
}

2.在项目目录下执行 ./yii 控制器名/action函数名 即可执行该函数

root@debian:/opt/lampp/htdocs/dev/advanced# ./yii hello/index
hello console!
root@debian:/opt/lampp/htdocs/dev/advanced# 

3.带参数的控制台程序:

public function actionHello($name)
    {
        echo "hello".$name."\n";
        //./yii hello/hello haha
    }

    public function actionHelloArray(array $names)
    {
        var_dump($names);
        /*
        root@debian:/opt/lampp/htdocs/dev/advanced# ./yii hello/hello-array haha,qi,a  
        array(3) {
          [0]=>
          string(4) "haha"
          [1]=>
          string(2) "qi"
          [2]=>
          string(1) "a"
        }
        */
    }

4.带选项的控制台程序:

<?php
namespace console\controllers;
use Yii;
use yii\console\Controller;
class HelloController extends Controller
{


//选项
    public $rev;
    public function options($actionID)
    {
        return ['rev'];
    }
    public function optionAliases()
    {
        return ['r'=>'rev'];
    }

    public function actionChoose()
    {
        if($this->rev==1){echo "1\n";}
        else{echo "2\n";}
    }
    /*
    *
    root@debian:/opt/lampp/htdocs/dev/advanced# ./yii hello/choose -r=1
1
root@debian:/opt/lampp/htdocs/dev/advanced# ./yii hello/choose -r=2
2
root@debian:/opt/lampp/htdocs/dev/advanced# ./yii hello/choose --rev=2
2
root@debian:/opt/lampp/htdocs/dev/advanced# ./yii hello/choose --rev=1
1
*/
    //选项end

    public function actionIndex()
    {
       echo "hello console!\n";
    }

    public function actionHello($name)
    {
        echo "hello".$name."\n";
        //./yii hello/hello haha
    }

    public function actionHelloArray(array $names)
    {
        var_dump($names);
    }

    //./yii hello/send 发邮件mailer
    public function actionSend(){
       // return "sendmail";
        $mail= Yii::$app->mailer->compose();
        $mail->setFrom('7138784@qq.com');   
        $mail->setTo('nbllq@qq.com'); 
        $mail->setSubject("mail-test");  
        //$mail->setTextBody('txtxtxtx');   //发布纯文字文本
        $mail->setHtmlBody("<br>askme");    //发布可以带html标签的文本
        if($mail->send())  
            return "success";  
        else  
            return "fail"; 
    }



}

基于yii2的blog系统开发9-授权ACF&RBAC

接上一节 基于yii2的blog系统开发8:
视频地址:https://v.youku.com/v_show/id_XMTgwODg1NzIzMg==.html
高级模版自动生成的User模型里面没有加email验证规则,建议用gii重新生成比对下,把相应代码补全.
在yii2中提供两种授权管理途径
(1)ACF存取控制授权(较为简单,适合访客权限设置简单的情景)
(2)RBAC基于角色的存取授权控制(较为复杂,适合博客,论坛等大型网站)

第十五步 ACF授权

acf即存取控制过滤器,控制器里引用:
use yii\filters\AccessControl
改写behaviors方法:

public function behaviors()
 {
     return [
        'access'=>[
          'class' => AccessControl::className(),
            //允许未登录的游客访问action ID为 index login的action
          'rules' => [
                [
                  'actions' => ['index','login'],
                   'allow' => true,
                   'roles' => ['?'],//?表示未登录的游客
                ],
              //只允许已经登录的用户访问action ID为contact about的action 其他禁止
                    [
                   'actions' => ['contact','about'],
                   'allow' => true,
                   'roles' => ['@'],// @表示已经登录的用户
                ]
                 ],
            ],
           ];
 }

更多详细设置参考:
https://blog.csdn.net/aozeahj/article/details/52404755

第十六步 RBAC授权

1.修改common/config/main.php:

<?php
return [
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
    'components' => [
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        //添加这个代码
        'authManager'=>[
            'class'=>'yii\rbac\DbManager',
        ],
    ],

];

2.在项目根目录执行命令,在数据库创建四张rbac的表:

./yii migrate --migrationPath=@yii/rbac/migrations

3.在console/controllers/下新建RbacController.php文件,主要目的为利用authManager组件的api来创建授权表的初始数据:

<?php
namespace console\controllers;
use Yii;
use yii\console\Controller;
class RbacController extends Controller
{
    public function actionInit()
    {
        $auth = Yii::$app->authManager;
        // 添加 "createPost" 权限
        $createPost = $auth->createPermission('createPost');
        $createPost->description = '新增文章';
        $auth->add($createPost);
        // 添加 "updatePost" 权限
        $updatePost = $auth->createPermission('updatePost');
        $updatePost->description = '修改文章';
        $auth->add($updatePost);
        // 添加 "deletePost" 权限
        $deletePost = $auth->createPermission('deletePost');
        $deletePost->description = '删除文章';
        $auth->add($deletePost);

        // 添加 "approveComment" 权限
        $approveComment = $auth->createPermission('approveComment');
        $approveComment->description = '审核评论';
        $auth->add($approveComment);

        // 添加 "postadmin" 角色并赋予 "updatePost" “deletePost” “createPost”
        $postAdmin = $auth->createRole('postAdmin');
        $postAdmin->description = '文章管理员';
        $auth->add($postAdmin);
        $auth->addChild($postAdmin, $updatePost);
        $auth->addChild($postAdmin, $createPost);
        $auth->addChild($postAdmin, $deletePost);

        // 添加 "postOperator" 角色并赋予  “deletePost” 
        $postOperator = $auth->createRole('postOperator');
        $postOperator->description = '文章操作员';
        $auth->add($postOperator);
        $auth->addChild($postOperator, $deletePost);

        // 添加 "commentAuditor" 角色并赋予  “approveComment”
        $commentAuditor = $auth->createRole('commentAuditor');
        $commentAuditor->description = '评论审核员';
        $auth->add($commentAuditor);
        $auth->addChild($commentAuditor, $approveComment);
        // 添加 "admin" 角色并赋予所有其他角色拥有的权限
        $admin = $auth->createRole('admin');
        $admin->description = '系统管理员';
        $auth->add($admin);
        $auth->addChild($admin, $postAdmin);
        $auth->addChild($admin, $commentAuditor);


        // 为用户指派角色。其中 1 和 2 是由 IdentityInterface::getId() 返回的id (译者注:user表的id)
        // 通常在你的 User 模型中实现这个函数。
        $auth->assign($admin, 1);
        $auth->assign($postAdmin, 2);
        $auth->assign($postOperator, 3);
        $auth->assign($commentAuditor, 4);
    }
}

在项目目录下执行命令:
./yii rbac/init
这样数据库里面就有数据了

4.在控制器中执行权限检查,以backend/controllers/PostController.php为例:

use yii\web\ForbiddenHttpException;
public function actionCreate()
    {
        if(!Yii::$app->user->can('createPost')){
            throw new ForbiddenHttpException('对不起,您没有该操作的权限');
        }
        $model = new Post();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }

5.写个界面给管理员用户分配权限,主要就是对对应四张表进行crud即可

5.1 backend/views/adminusesr下新建授权页面sq.php:

<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>

<div class="adminuser-form">
    <?php $form = ActiveForm::begin(); ?>   
<!--?= Html::checkboxList('roles', '选中值数组',['value'=>'label']) ?-->
  <?= Html::checkboxList('roles', $a1,$a2) ?>   
    <div class="form-group">
        <?= Html::submitButton('保存', ['class' => 'btn btn-success']) ?>
    </div>
    <?php ActiveForm::end(); ?>
</div>

5.2 用gii生成auth_assignment,auth_item的模型类,然后在backend/controllers/adminusesrController.php中添加权限管理的action,注意这里的函数名和index视图中的权限管理按钮名称对应:

use yii\helpers\ArrayHelper;
public function actionQxgl($id)
    {

        $y1=AuthAssignment::find()->select(['item_name'])->where(['user_id' => $id])->all();
        $y2=yii::$app->db->createCommand("select name,description from auth_item where type=1")->query();

        $a1 = array();
        foreach($y1 as $i){
            //VarDumper::dump($i);exit(0);
            array_push($a1,$i->item_name);
        }

        $a2 = ArrayHelper::map($y2,'name','description');


        if(isset($_POST['roles']))
        {
            AuthAssignment::deleteAll('user_id=:id',[':id'=>$id]);
            $newr=$_POST['roles'];
            $len=count($newr);
            for($i=0;$i<$len;$i++){
                $t = new AuthAssignment();
                $t->item_name=$newr[$i];
                $t->user_id=$id;
                $t->created_at=time();
                $t->save();
            }

            return $this->redirect(["index"]);
        }

        return $this->render('sq', ['id'=>$id,
            'a1' => $a1,'a2' => $a2,
        ]);
    }

基于yii2的blog系统开发8-用户认证

接上一篇:基于yii2的blog系统开发7-评论审核:

第十四步 用户认证

高级模版装好以后,前端frontend的用户认证就已经完成了

常用代码说明:
//检测当前用户身份
$identity = Yii::$app->user->identity;

//获取用户id,未认证用户为Null
$id = Yii::$app->user->id;

//判断当前用户是否是游客
$isGuest = Yii::$app->user->isGuest;

//将当前用户的身份登记到yii\web\User。根据设置用session或cookie记录用户身份,用户的认证状态将在整个会话中得以维持。
Yii::$app->user->login($identity);

//注销用户
Yii::$app->user->logout();

后端用户认证

前端用户的默认认证类是User,对应前端用户的user表,后端用户表是adminuser,而后端用户的默认模版安装的认证类还是user显然就不对了,所以这儿

1.修改backend/config/main.php:

'identityClass' => 'common\models\Adminuser',

另外,要实现后端用户认证可以参考前端用户认证代码来改。

2.修改common/models/Adminuser.php:

<?php

namespace common\models;

use Yii;
use yii\web\IdentityInterface;
/**
 * This is the model class for table "adminuser".
 *
 * @property int $id
 * @property string $username
 * @property string $nickname
 * @property string $password
 * @property string $email
 * @property string|null $profile
 * @property string $auth_key
 * @property string $password_hash
 * @property string|null $password_reset_token
 *
 * @property Post[] $posts
 */
class Adminuser extends \yii\db\ActiveRecord implements IdentityInterface
{
    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return 'adminuser';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['username', 'nickname', 'password', 'email', 'auth_key', 'password_hash'], 'required'],
            [['profile'], 'string'],
            [['username', 'nickname', 'password', 'email'], 'string', 'max' => 128],
            [['auth_key'], 'string', 'max' => 32],
            [['password_hash', 'password_reset_token'], 'string', 'max' => 255],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'username' => 'Username',
            'nickname' => 'Nickname',
            'password' => 'Password',
            'email' => 'Email',
            'profile' => 'Profile',
            'auth_key' => 'Auth Key',
            'password_hash' => 'Password Hash',
            'password_reset_token' => 'Password Reset Token',
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getPosts()
    {
        return $this->hasMany(Post::className(), ['author_id' => 'id']);
    }

    /**
     * {@inheritdoc}
     */
    public static function findIdentity($id)
    {
        return static::findOne(['id' => $id]);
    }

    /**
     * {@inheritdoc}
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
    }

    /**
     * Finds user by username
     *
     * @param string $username
     * @return static|null
     */
    public static function findByUsername($username)
    {
        return static::findOne(['username' => $username]);
    }

    /**
     * Finds user by password reset token
     *
     * @param string $token password reset token
     * @return static|null
     */
    public static function findByPasswordResetToken($token)
    {
        if (!static::isPasswordResetTokenValid($token)) {
            return null;
        }

        return static::findOne([
            'password_reset_token' => $token,
           // 'status' => self::STATUS_ACTIVE,
        ]);
    }

    /**
     * Finds user by verification email token
     *
     * @param string $token verify email token
     * @return static|null
     */
    public static function findByVerificationToken($token) {
        return static::findOne([
            'verification_token' => $token,
           // 'status' => self::STATUS_INACTIVE
        ]);
    }

    /**
     * Finds out if password reset token is valid
     *
     * @param string $token password reset token
     * @return bool
     */
    public static function isPasswordResetTokenValid($token)
    {
        if (empty($token)) {
            return false;
        }

        $timestamp = (int) substr($token, strrpos($token, '_') + 1);
        $expire = Yii::$app->params['user.passwordResetTokenExpire'];
        return $timestamp + $expire >= time();
    }

    /**
     * {@inheritdoc}
     */
    public function getId()
    {
        return $this->getPrimaryKey();
    }

    /**
     * {@inheritdoc}
     */
    public function getAuthKey()
    {
        return $this->auth_key;
    }

    /**
     * {@inheritdoc}
     */
    public function validateAuthKey($authKey)
    {
        return $this->getAuthKey() === $authKey;
    }

    /**
     * Validates password
     *
     * @param string $password password to validate
     * @return bool if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }

    /**
     * Generates password hash from password and sets it to the model
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    }

    /**
     * Generates "remember me" authentication key
     */
    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }

    /**
     * Generates new password reset token
     */
    public function generatePasswordResetToken()
    {
        $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
    }

    /**
     * Generates new token for email verification
     */
    public function generateEmailVerificationToken()
    {
        $this->verification_token = Yii::$app->security->generateRandomString() . '_' . time();
    }

    /**
     * Removes password reset token
     */
    public function removePasswordResetToken()
    {
        $this->password_reset_token = null;
    }
}

3.修改backend/controllers/siteController.php:

LoginForm已经被前端用了,所以这儿用一个新的BackLoginForm

use common\models\BackLoginForm;
public function actionLogin()
{...
$model = new BackLoginForm();
...
}

4.把common/models/LoginForm.php拷贝一份,重新命名为BackLoginForm.php并修改对应代码:

class BackLoginForm extends Model
{
 public function attributeLabels()
    {
        return [
            'username' => '用户名',
            'password' => '密码',
            'rememberMe'=>'记住密码',
        ];
    }

 protected function getUser()
    {
        if ($this->_user === null) {
            $this->_user = Adminuser::findByUsername($this->username);
        }

        return $this->_user;
    }
}

5.汉化视图backend/views/site/login.php: