一张图看懂开源许可协议,开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别

一张相当直观清晰的图来划分各种协议:开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别

以下是上述协议的简单介绍:
BSD开源协议
BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。

但”为所欲为”的前提当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:

如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。
如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。

BSD 代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发。

Apache Licence 2.0
Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。需要满足的条件也和BSD类似:

需要给代码的用户一份Apache Licence
如果你修改了代码,需要再被修改的文件中说明。
在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明。
如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改。

Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。
GPL

我们很熟悉的Linux就是采用了GPL。GPL协议和BSD, Apache Licence等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux,包括商业公司的linux和linux上各种各样的由个人,组织,以及商业软件公司开发的免费软件了。

GPL协议的主要内容是只要在一个软件中使用(”使用”指类库引用,修改后的代码或者衍生代码)GPL 协议的产品,则该软件产品必须也采用GPL协议,既必须也是开源和免费。这就是所谓的”传染性”。GPL协议的产品作为一个单独的产品使用没有任何问题,还可以享受免费的优势。

由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议,对于使用GPL协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。

其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似。

LGPL
LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生之GPL类库的的软件必须采用GPL协议不同。LGPL 允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并发布和销售。

但是如果修改LGPL协议的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生的代码都必须采用LGPL协议。因此LGPL协议的开源代码很适合作为第三方类库被商业软件引用,但不适合希望以LGPL协议代码为基础,通过修改和衍生的方式做二次开发的商业软件采用。

GPL/LGPL都保障原作者的知识产权,避免有人利用开源代码复制并开发类似的产品

MIT
MIT是和BSD一样宽范的许可协议,作者只想保留版权,而无任何其他了限制.也就是说,你必须在你的发行版里包含原许可协议的声明,无论你是以二进制发布的还是以源代码发布的.

MPL
MPL是The Mozilla Public License的简写,是1998年初Netscape的 Mozilla小组为其开源软件项目设计的软件许可证。MPL许可证出现的最重要原因就是,Netscape公司认为GPL许可证没有很好地平衡开发者对源代码的需求和他们利用源代码获得的利益。同著名的GPL许可证和BSD许可证相比,MPL在许多权利与义务的约定方面与它们相同(因为都是符合OSIA 认定的开源软件许可证)。但是,相比而言MPL还有以下几个显著的不同之处:

◆ MPL虽然要求对于经MPL许可证发布的源代码的修改也要以MPL许可证的方式再许可出来,以保证其他人可以在MPL的条款下共享源代码。但是,在MPL 许可证中对“发布”的定义是“以源代码方式发布的文件”,这就意味着MPL允许一个企业在自己已有的源代码库上加一个接口,除了接口程序的源代码以MPL 许可证的形式对外许可外,源代码库中的源代码就可以不用MPL许可证的方式强制对外许可。这些,就为借鉴别人的源代码用做自己商业软件开发的行为留了一个豁口。
◆ MPL许可证第三条第7款中允许被许可人将经过MPL许可证获得的源代码同自己其他类型的代码混合得到自己的软件程序。
◆ 对软件专利的态度,MPL许可证不像GPL许可证那样明确表示反对软件专利,但是却明确要求源代码的提供者不能提供已经受专利保护的源代码(除非他本人是专利权人,并书面向公众免费许可这些源代码),也不能在将这些源代码以开放源代码许可证形式许可后再去申请与这些源代码有关的专利。
◆ 对源代码的定义
而在MPL(1.1版本)许可证中,对源代码的定义是:“源代码指的是对作品进行修改最优先择取的形式,它包括:所有模块的所有源程序,加上有关的接口的定义,加上控制可执行作品的安装和编译的‘原本’(原文为‘Script’),或者不是与初始源代码显著不同的源代码就是被源代码贡献者选择的从公共领域可以得到的程序代码。”
◆ MPL许可证第3条有专门的一款是关于对源代码修改进行描述的规定,就是要求所有再发布者都得有一个专门的文件就对源代码程序修改的时间和修改的方式有描述

转自https://www.cnblogs.com/newcaoguo/p/7103249.html

html页面在线预览PDF文件

1,使用HTML 5 标签
2,在c盘放一个pdf文件,如test.pdf
3,编写html页面

<!DOCTYPE html>
<html>
<head>
    <title>pdf</title>
</head>
<body>
    <embed src="c:\test.pdf#page=8" type="application/pdf" style="overflow: auto; position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%;">
</body>
</html>

Jupyter 安装 nbextensions 插件

nbextensions 是 Jupyter 非常好的插件,它是将一系列 js 脚本嵌入到 Jupyter 中,增强 Jupyter 的交互式体验,可以让你的 Jupyter 变得非常强大。

pip install jupyter_contrib_nbextensions
jupyter contrib nbextension install --user
#启用 nbextensions
jupyter nbextension enable codefolding/main

执行上述命令后,重启你的 Jupyter Notebook ,打开 就可以看到 nbextensions 插件的配置页面了。

常用的插件
Code Prettify
Collapsible headings
Highlight selected word
Nofity
Ruler
Snippets Menu
table_beautifier
Codefolding
Execute Time
Hide Header
highlighter
Nbextensions dasnboard tab
Runtools
Toggle all line numbers
contrib_nbextensions_help_item
Freeze
Hide Input
Nbextensions edit menu item
Python Markdown
Scratchpad
Tree filter
datestamper
Hide input all
Scroll Down
Snippets
Table of Content
Variable Inspector

OpenCV-python进行多个摄像头同步采集并拼接显示(多个视频拼接到一个窗口显示)

以下是两个摄像头进行拼接的代码,同理可以拼接N个。

效果如下:由于两个摄像头分辨率不一样,以及懒得resize成一样的大小,显示效果左边视频上下留黑了。

import cv2
import numpy as np
 
##选择摄像头
videoLeftUp = cv2.VideoCapture(0)
videoRightUp = cv2.VideoCapture(1)
 
width = (int(videoLeftUp.get(cv2.CAP_PROP_FRAME_WIDTH)))
height = (int(videoLeftUp.get(cv2.CAP_PROP_FRAME_HEIGHT)))
 
while (videoLeftUp.isOpened()):
    retLeftUp, frameLeftUp = videoLeftUp.read()
    retRightUp, frameRightUp = videoRightUp.read()
 
    frameLeftUp = cv2.resize(frameLeftUp, (int(width), int(height)), interpolation=cv2.INTER_CUBIC)
    frameRightUp = cv2.resize(frameRightUp, (int(width), int(height)), interpolation=cv2.INTER_CUBIC)
 
    frameUp = np.hstack((frameLeftUp, frameRightUp))
 
    cv2.imshow('frame', frameUp)
    print('111')
    key = cv2.waitKey(10)
    if int(key) == 113:
        break
 
videoLeftUp.release()
videoRightUp.release()

在下面再加两个视频窗口可以采用上下拼接:

frameUp = np.hstack((frameLeftUp, frameRightUp))
    frameDown = np.hstack((frameLeftDown, frameRightDown))
    frame = np.vstack((frameUp, frameDown))

————————————————

原文链接:https://blog.csdn.net/weixin_43002202/article/details/95459379

ipython notebook 修改一开始打开的文件夹路径

1. 首先输出配置文件
jupyter notebook –generate-config#输出配置文件
2.编辑配置文件
找到输出的配置文件后,找到#The directory to use for notebooks and kernels.这一行,将下面的c.NotebookApp.notebook_dir = ”改为c.NotebookApp.notebook_dir = ‘目标路径’

关系型数据库设计:三大范式的通俗理解

目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。

而通常我们用的最多的就是第一范式(1NF)、第二范式(2NF)、第三范式(3NF),也就是本文要讲的“三大范式”。

第一范式(1NF):要求数据库表的每一列都是不可分割的原子数据项。

举例说明:

在上面的表中,“家庭信息”和“学校信息”列均不满足原子性的要求,故不满足第一范式,调整如下:

可见,调整后的每一列都是不可再分的,因此满足第一范式(1NF);

第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)

第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。

举例说明:

在上图所示的情况中,同一个订单中可能包含不同的产品,因此主键必须是“订单号”和“产品号”联合组成,

但可以发现,产品数量、产品折扣、产品价格与“订单号”和“产品号”都相关,但是订单金额和订单时间仅与“订单号”相关,与“产品号”无关,

这样就不满足第二范式的要求,调整如下,需分成两个表:

第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

举例说明:

上表中,所有属性都完全依赖于学号,所以满足第二范式,但是“班主任性别”和“班主任年龄”直接依赖的是“班主任姓名”,

而不是主键“学号”,所以需做如下调整:

这样以来,就满足了第三范式的要求。

ps:如果把上表中的班主任姓名改成班主任教工号可能更确切,更符合实际情况,不过只要能理解就行。

ubuntu samba服务配置(一)

samba是一个实现不同操作系统之间文件共享和打印机共享的一种SMB协议的免费软件。

samba软件结构:

/etc/samba/smb.conf                 #samba服务的主要配置文件

/etc/samba/lmhosts                #samba服务的域名设定,主要设置IP地址对应的域名,类似linux系统的/etc/hosts

/etc/samba/smbusers                #samba服务设置samba虚拟用户的配置文件

/var/log/samba                 #samab服务存放日志文件

/var/lib/samba/private/{passdb.tdb,secrets.tdb}     #存放samba的用户账号和密码数据库文档

Samba在线安装

[root@VMredhat6 ~]# yum -y install samba* #yum在线安装samba

[root@VMredhat6 ~]# rpm -qa | grep samba #检查samba安装情况

samba-client-3.6.23-41.el6.x86_64

samba-winbind-clients-3.6.23-41.el6.x86_64

samba-3.6.23-41.el6.x86_64

samba4-libs-4.0.0-58.el6.rc4.x86_64

samba-common-3.6.23-41.el6.x86_64

samba-winbind-3.6.23-41.el6.x86_64

标注:Samab服务开启之前需要关闭两个服务,iptables防火墙(如果你熟悉可以不关闭,放行smb的端口即可,SAMBA服务TCP端口139,445 UDP端口 137,138);selinux服务。

关闭防火墙:

[root@VMredhat6 ~]# /etc/init.d/iptables stop #关闭防火墙

[root@VMredhat6 ~]# chkconfig –list iptables #查看防火墙启动状态

[root@VMredhat6 ~]# chkconfig iptables off #关闭防火墙开机自动启动

关闭selinux:

[root@VMredhat6 ~]# vim /etc/selinux/config #永久关闭selinux

set SELINUX=disabled #SELINUX=enforcing改为SELINUX=disabled

标注:smb.conf配置文件分为两大类,一个全局设置参数,一个是文件共享设置参数:

#全局设置参数

[global]

config file = /etc/samba/smb.conf.%U #可以让你使用另一个配置文件来覆盖缺省的配置文件。如果文件 不存在,则该项无效。

workgroup = WORKGROUP                  #工作组名称

server string = Samba Server Version %v                #主机的简易说明

netbios name = MYSERVER                #主机的netBIOS名称,如果不填写则默认服务器DNS的一部分,workgroup和netbios name名字不要设置成一样

interfaces = lo eth0 192.168.12.2/24 192.168.13.2/24 #设置samba服务器监听网卡,可以写网卡名称或IP地址,默认注释

hosts allow = 127. 192.168.12. 192.168.13. #设置允许连接到samba服务器的客户端,默认注释

hosts deny =192.168.12.0/255.255.255.0 #设置不允许连接到samba服务器的客户端,默认注释

log level =1 #日志文件安全级别,0~10级别,默认0

log file = /var/log/samba/%m                      #产生日志文件的命名,默认以访问者IP地址命名

max log size = 50                          #日志文件最大容量50,默认50,单位为KB,0表示不限制

security = share                          #设置用户访问samba服务器的验证方式 ,一共四种验证方式。

1. share:用户访问Samba Server不需要提供用户名和口令, 安全性能较低。

2. user:Samba Server共享目录只能被授权的用户访问,由Samba Server负责检查账号和密码的正确性。账号和密码要在本Samba Server中建立。

3. server:依靠其他Windows NT/2000或Samba Server来验证用户的账号和密码,是一种代理验证。此种安全模式下,系统管理员可以把所有的Windows用户

和口令集中到一个NT系统上,使用Windows NT进行Samba认证, 远程服务器可以自动认证全部用户和口令,如果认证失败,Samba将使用用户级安全模式作为替代的方式。

4. domain:域安全级别,使用主域控制器(PDC)来完成认证。

passdb backend = tdbsam                       #定义用户后台类型

1、smbpasswd:使用SMB服务的smbpasswd命令给系统用户设置SMB密码

2、tdbsam:创建数据库文件并使用pdbedit建立SMB独立用户,smbpasswd –a username建立samba用户并设置密码,不过建立samba用户

必须先建立系统用户,也可以使用pdbedit命令来建立samba用户:

pdbedit –a username:新建Samba账户。
samba服务配置(一)
pdbedit –x username:删除Samba账户。

pdbedit –L:列出Samba用户列表,读取passdb.tdb数据库文件。

pdbedit –Lv:列出Samba用户列表的详细信息。

pdbedit –c “[D]” –u username:暂停该Samba用户的账号。

pdbedit –c “[]” –u username:恢复该Samba用户的账号。

3、ldapsam:基于LDAP服务进行账户验证

username map = /etc/samba/smbusers #配合/etc/samba/smbusers文件设置虚拟用户

#共享文件设置参数

[share]                                #自定义共享名称

comment = This is share software                    #共享描述

path = /home/testfile               #共享目录路径

browseable = yes/no                  #设置共享是否可浏览,如果no就表示隐藏,需要通过IP+共享名称进行访问

writable = yes/no                              #设置共享是否具有可写权限

read only = yes/no                             #设置共享是否具有只读权限

admin users = root                             #设置共享的管理员,如果security =share 时,引项无效,多用户中间使用逗号隔开,例如admin users = root,user1,user2

valid users = username                           #设置允许访问共享的用户,例如valid users = user1,user2,@group1,@group2(多用户或组使用逗号隔开,@group表示group用户组)

invalid users = username                          #设置不允许访问共享的用户

write list = username                            #设置在共享具有写入权限的用户,例如例如write list = user1,user2,@group1,@group2(多用户或组使用逗号隔开,@group表示group用户组)

public = yes/no                              #设置共享是否允许guest账户访问

guest ok = yes/no                              #功能同public 一样

create mask = 0700                         #创建的文件权限为700

directory mode = 0700                         #创建的文件目录为 700

一、不需要账号密码访问的共享(security = share )

[root@VMredhat6 samba]# /etc/init.d/smb start              #启动samba服务

[root@VMredhat6 samba]# ls -ld /home/smbuser/            #查看共享文件权限

[root@VMredhat6 samba]# chmod 777 /home/smbuser/           #分配共享文件最大权限

[root@VMredhat6 samba]# testparm               #检查smb.conf配置文件是否有语法错误

[root@VMredhat6 samba]# netstat -tlnp | grep mbd             #查看samba端口,TCP端口139,445 UDP端口 137,138

[root@VMredhat6 samba]# vim /etc/samba/smb.conf            #设置smb.conf配置文件

[global]

workgroup = WORKGROUP                    #设置主机工作组

server string = Samba Server Version %v                 #samba服务注释

log level = 1                       #设置日志文件安全级别为1

log file = /var/log/samba/%m                    #设置日志文件名称,%m以IP地址为名称

max log size = 50                       #设置日志文件最大容量50KB,0表示不限制

security = share                      #以share验证方式访问

passdb backend = tdbsam                    #定义用户后台类型

load printers = no                      #关闭打印共享功能

cups options = raw                      #打印机选项

[usershare]

comment = Home Directories                   #共享文件描述

path = /home/shareuser                    #共享路径

browseable = yes                     #共享文件可以浏览

writable = yes                      #共享文件可写

; read only = yes                     #共享文件可读,不能与writable共用

guest ok = yes                      #允许guest用户访问

[root@VMredhat6 samba]# /etc/init.d/smb restart             #重启samba服务

测试:

Windows系统操作:

在win 7系统打开运行输入 \\192.168.10.150 直接访问,不需要输入任何用户和密码

在linux操作需要进行挂载:

[root@VMredhat6 ~]# yum -y install samba-client                #在线安装samba-client才能使用smbclient命令

[root@VMredhat6 ~]# smbclient -L //192.168.10.150               #查看samba服务器共享目录

[root@VMredhat6 ~]# mount -t cifs //192.168.10.150/usershare /mnt/share     #挂载

二、需要输入账号和密码访问的共享(security = user)

标注:创建samba用户之前必须先创建系统用户,系统用户和samba用户名一样,但密码可以设置成不一样

[root@VMredhat6 samba]# useradd test01              #创建系统用户test01

[root@VMredhat6 samba]# useradd test02                 #创建系统用户test02

[root@VMredhat6 samba]# passwd test01                 #设置系统用户test01密码

[root@VMredhat6 samba]# passwd test01                  #设置系统用户test02密码

[root@VMredhat6 samba]# smbpasswd -a test01                 #把系统用户test01添加为samba用户并设置samba用户登录密码

[root@VMredhat6 samba]# smbpasswd -a test02                 #把系统用户test02添加为samba用户并设置samba用户登录密码

[root@VMredhat6 samba]# pdbedit –L                   #查看samba用户

[root@VMredhat6 samba]# mkdir /home/{smbadmin01,smbuser01}         #home创建两个共享目录

[root@VMredhat6 samba]# chmod 777 /home/{smbadmin01,smbuser01}       #目录分配最高权限

[root@VMredhat6 ~]# vim /etc/samba/smb.conf                 #设置smb.conf配置文件

[global]

workgroup = WORKGROUP #设置主机工作组

server string = Samba Server Version %v #samba服务注释

log level = 1                          #设置日志文件安全级别为1

log file = /var/log/samba/%m                        #设置日志文件名称,%m以IP地址为名称

max log size = 50                          #设置日志文件最大容量50KB,0表示不限制

security = user                          #以user验证方式访问

passdb backend = tdbsam                        #定义用户后台类型

load printers = no                           #关闭打印共享功能

cups options = raw                           #打印机选项

[usershare]

comment = Home Directories                        #共享文件描述

path = /home/smbuser01                          #共享路径

browseable = yes                            #共享文件可以浏览

read only = yes                            #共享文件可读,不能与writable共用

valid users = test01,test02                          #设置允许访问共享用户

[adminshare]

comment = Home Directories                         #共享文件描述

path = /home/smbadmin01                          #共享路径

browseable = yes                             #共享文件可以浏览

writable = yes                              #共享文件可写

create make = 0664                           #设置用户创建文件权限0664

directory make = 0775                           #设置用户创建目录权限0775

write list = test01                              #设置允许访问共享具有写入权限的用户

[root@VMredhat6 samba]# /etc/init.d/smb restart                   #重启samba服务

测试:

Windows系统操作:

在win 7系统打开运行输入 \\192.168.10.150 直接访问,输入test01用户和密码

在linux操作需要进行挂载:

[root@VMredhat6 ~]# yum -y install samba-client                  #在线安装samba-client才能使用smbclient命令

[root@VMredhat6 ~]# smbclient -L //192.168.10.150                 #查看samba服务器共享目录

[root@VMredhat6 ~]# mount -t cifs //192.168.10.150/usershare /mnt/share       #挂载

三、特殊操作:(samba用户映射,又称作虚拟用户)

  上述操作是创建系统用户再分配对应的samab用户,通过samba用户就能知道你的系统用户,缺乏一定的安全性。不过,

samba提供了一种方法,就是把samba用户映射成虚拟用户。比如linux系统创建了一个用户user,samba把user添加为samba用户,

samba再把user用户虚拟成user01,user02等用户,一个samba用户可以虚拟成一个或多个虚拟用户。这样就可以保证系统用户安全性。

操作跟上述操作一样,只是修改一些地方即可:

[root@VMredhat6 ~]# vim /etc/samba/smb.conf           #设置smb.conf配置文件

security = user                      #以user验证方式访问

passdb backend = tdbsam                   #定义用户后台类型

username map = /etc/samba/smbusers                  #添加这行配置,开启samba虚拟用户配置功能

[root@VMredhat6 samba]# vim /etc/samba/smbusers              #添加samba虚拟用户

# Unix_name = SMB_name1 SMB_name2 …

root = administrator admin

nobody = guest pcguest smbguest

test01 = user01 user02 user03

[root@VMredhat6 samba]# /etc/init.d/smb restart             #重启samba服务

测试:

Windows系统操作:

在win 7系统打开运行输入 \\192.168.10.150 直接访问,输入user01用户,密码是test01用户密码。

ubuntu samba 权限设置

ubuntu samba 权限设置1

一、samba 要求

公司有财务,技术,领导3个部门,我们分别为3个部门建立3个用户组为caiwu,network,lingdao;

三个部门里各有2个用户,我们建用户分别为 caiwu01,caiwu02,network01,network02,lingdao01,lingdao02

然后我们分别就公司的具体情况建立相应的目录及访问权限,通过以下的例子,希望大家能在平时的工作中灵活的应用samba的安全权限来设置你们的 samba 文件服务器。

1。首先服务器采用用户验证的方式,每个用户可以访问自己的宿主目录,并且只有该用户能访问宿主目录,并具有完全的权限,而其他人不能看到你的宿主目录。

2。建立一个 caiwu 的文件夹,希望 caiwu 组和 lingdao 组的人能看到,network02 也可以访问,但只有 caiwu01 有写的权限。

3。建立一个 lingdao 的目录,只有领导组的人可以访问并读写,还有 network02 也可以访问,但外人看不到那个目录

4。建立一个文件交换目录 exchange,所有人都能读写,包括 guest 用户,但每个人不能删除别人的文件。

5。建立一个公共的只读文件夹 public,所有人只读这个文件夹的内容。

二、操作

#sudo groupadd caiwu

#sudo groupadd network

#sudo groupadd lingdao

#sudo useradd caiwu01 -g caiwu

#sudo useradd caiwu02 -g caiwu

#sudo useradd network01 -g network

#sudo useradd network02 -g network

#sudo useradd lingdao01 -g lingdao

#sudo useradd lingdao02 -g lingdao

然后我们编辑用户密码

sudo smbpasswd -a caiwu01

sudo smbpasswd -a caiwu02

sudo smbpasswd -a network01

sudo smbpasswd -a network02

sudo smbpasswd -a lingdao01

sudo smbpasswd -a lingdao02

删除用户

sudo smbpasswd -x username

创建用户的自己目录

sudo mkdir /home/caiwu01
sudo mkdir /home/caiwu02
sudo mkdir /home/lingdao01
sudo mkdir /home/lingdao02
sudo mkdir /home/network01
sudo mkdir /home/network02

修改用户目录权限

sudo chmod -R 777 /home/caiwu01
sudo chmod -R 777 /home/caiwu02
sudo chmod -R 777 /home/lingdao01
sudo chmod -R 777 /home/lingdao02
sudo chmod -R 777 /home/network01
sudo chmod -R 777 /home/network02

创建用户的共享目录

#sudo mkdir /home/samba

#sudo mkdir /home/samba/caiwu

#sudo mkdir /home/samba/lingdao

#sudo mkdir /home/samba/exchange

#sudo mkdir /home/samba/public

我们为了避免麻烦可以在这里把上面所有的文件夹的权限都设置成777,我们通过samba灵活的权限管理来设置上面的5点要求。

sudo chmod -R 777 /home/samba
sudo chmod -R 777 /home/samba/*
sudo chmod -R 1777 /home/samba/exchange

注意这里权限是1777,类似的系统目录/tmp也具有相同的权限,这个权限能实现每个人能自由写文件,但不能删除别人的文件

三、sudo vim /etc/samba/smb.conf

[global]
workgroup = WORKGROUP
display charset = UTF-8
unix charset = UTF-8
dos charset = cp936

在最后加入

[homes]
comment = Home Directories
browseable = no
writable = yes
valid users = %S
create mode = 0664
directory mode = 0775

[caiwu]
comment = caiwu
path = /home/samba/caiwu
public = no
read list = @caiwu,network02,@lingdao #network 用户组只读权限,@表示的是一个工作组
write list = caiwu01 #caiwu01 可读权限的用户

[lingdao]
comment = Lingdao
path = /home/samba/lingdao
public = no
read list = network02
write list = @lingdao

[exchage]
comment = Exchange File Directory
path = /home/samba/exchange
public = yes
writable = yes

[Public]
comment = read only Public
path = /home/samba/public
public = yes
read only = yes

ubuntu samba 权限设置2
[AA]
comment = AA
path = /samba/A
public = no
valid users = b02,b01,c01,a01,@A
write list = @A
printable = no

[BB]
comment = BB
path = /samba/B
public = no
valid users = a01,c01,b01,@B
printable = no
write list = @B

[CC]
comment = CC
path = /samba/C
public = no
valid users = a01,b01,c01,@C
printable = no
write list = @C

[public-readonly]
comment = public-readonly
path = /samba/public-readonly
public = yes
read only = yes

[share-rw]
comment = share-rw
path = /samba/share-rw
public = yes
writable = yes
#sudo chmod 1777 /samba/share-rw

secure_filename 对中文不支持的处理

采用了对源码进行修改,增加对中文的支持。
使用环境 werkzeug 0.16.0 和 python 3.7。
关于 secure_filename() 的过滤代码写在 \Python36\Lib\site-packages\werkzeug\utils.py
初始代码

if isinstance(filename, text_type):
    from unicodedata import normalize
    filename = normalize('NFKD', filename).encode('ascii', 'ignore') # 转码
    if not PY2:
        filename = filename.decode('ascii') # 解码
for sep in os.path.sep, os.path.altsep:
    if sep:
        filename = filename.replace(sep, ' ')

filename = str(_filename_ascii_strip_re.sub('', '_'.join( # 正则
               filename.split()))).strip('._')

修改后代码
37行
正则增加对汉字和日语假名的部分
\u4E00-\u9FBF 中文
\u3040-\u30FF 假名
\u31F0-\u31FF 片假名扩展

_filename_ascii_add_strip_re = re.compile(r'[^A-Za-z0-9_\u4E00-\u9FBF\u3040-\u30FF\u31F0-\u31FF.-]')

384行 的两个”ascii”修改为 “utf-8”,两个都要修改

 if isinstance(filename, text_type):
     from unicodedata import normalize
     filename = normalize('NFKD', filename).encode('utf-8', 'ignore') # 转码
     if not PY2:
         filename = filename.decode('utf-8') # 解码
 for sep in os.path.sep, os.path.altsep:
     if sep:
         filename = filename.replace(sep, ' ')

原文链接:https://blog.csdn.net/qq_30490489/article/details/92000197

flask-login和flask-wtf

一、表单验证
前言
在Flask项目开发中针对提交表单的校验,可以使用Flask-WTF扩展库进行快速的字段校验,也可以进行页面快速渲染,并提供跨站请求伪造的保护功能。

1. 安装Flask-WTF

pip install flask-wtf

注意:
可以新建一个requirement.txt文档,里面写入将要导入的包

flask
flask-script
flask-blueprint
flask-sqlalchemy
pymysql
redis
flask-session

在Terminal中输入:pip install -r requirement.txt

2. 实现注册功能

2.1 注册表单模型定义
在定义的表单类中定义需要验证的username、password和password2字段,并实现如下校验:

校验密码password2和password相等
校验用户名是否存在
校验用户名的长度是否符合规范

users->forms.py
“`
# 导入扩展类
from flask_wtf import FlaskForm
# 导入验证字段
from wtforms import StringField, SubmitField, ValidationError
# 导入表单验证
from wtforms.validators import DataRequired, EqualTo

 from user.models import User

 class UserForm(FlaskForm):
     """
     登录注册表单验证
     """
     username = StringField('用户名', validators=[DataRequired()])
     password = StringField('密码', validators=[DataRequired()])
     password2 = StringField('确认密码', validators=[DataRequired(),
                                                 EqualTo('password', '密码不一致')]
                             )
     submit = SubmitField('提交')

     def validate_username(self, field):
         # 验证用户名是否重复
         if User.query.filter(User.username == field.data).first():
             raise ValidationError('用户名已存在')

         # 对用户名的长度进行判断
         if len(field.data) < 3:
             raise ValidationError('用户名长度不能少于3个字符')

         if len(field.data) > 6:
             raise ValidationError('用户名长度不能大于6个字符')

```

注意: 验证字段的方法名为: validate_字段(self, field)

2.2 定义注册视图函数
当HTTP请求为GET时,将表单验证对象返回给页面。
当HTTP请求为POST时,通过方法validate_on_submit()方法进行字段校验和提交判断,如果校验失败,则可以从form.errors中获取错误信息。
如果验证通过,则从form.字段.data中获取到字段的值。
users->views

@blue.route('/register/', methods=['GET', 'POST'])
def register():
    form = UserForm()
    if request.method == 'GET':
        return render_template('register.html', form=form)

    if request.method == 'POST':
        # 判断表单中的数据是否通过验证
        if form.validate_on_submit():
            # 获取验证通过后的数据
            username = form.username.data
            password = form.password.data
            # 保存
            user = User()
            user.username = username
            #加密 generate_password_hash(password)
            user.password = generate_password_hash(password)
            user.save()
            return redirect(url_for('user.login'))
        return render_template('register.html', form=form)

2.3 模板展示
注册模板采用继承父模板base.html的形式。在register.html模压中分析如下:

1\. 定义字段名: {{ form.字段.label }}

2\. 定义input输入框: {{ form.字段 }}

3\. 展示错误信息: {{ form.errors.字段 }}

4\. 跨站请求伪造: {{ form.csrf_token }}

注册register.html页面如下:

{% extends 'base.html' %}

{% block title %}
    注册页面
{% endblock %}

{% block content %}
    <form action="" method="post">
        {{ form.csrf_token }}
        {{ form.username.label }}:{{ form.username(style='color:red;', placeholder='请输入用户名', onblur="alert('123')") }}
        {{ form.password.label }}:{{ form.password }}
        {{ form.password2.label }}:{{ form.password2 }}
        {{ form.submit }}

        {% if form.errors %}
            姓名错误信息:{{ form.errors.username }}
            密码错误信息:{{ form.errors.password2 }}
        {% endif %}
    </form>
{% endblock %}

注意: 通过form.字段解析的input标签中可以自定义样式,如{{ form.字段(class=’xxx’, style=’color:red’) }}

3. 常见字段类型

字段类型    说明
StringField 普通文本字段
PasswordField   密码文本字段
SubmitField 提交按钮
HiddenField 隐藏文本字段
TextAreaField   多行文本字段
DateField   文本字段,datetime.date格式
DateTimeField   文本字段,datetime.datetime格式
IntegerField    文本字段,整数类型
FloatField  文本字段,小数类型
BooleanField    复选框,值为True或False
RadioField  单选框
SelectField 下拉列表
FileField   文件上传字段

4. 验证器

验证器 说明
DataRequired    确保字段有值(并且if判断为真)
Email   邮箱地址
IPAddress   IPv4的IP地址
Length  规定字符长度
NumberRange 输入数值的范围
EqualTo 验证两个字段的一致性
URL 有效的URL
Regexp  正则验证

二、登录注册
前言
在flask中如何快速的实现登录注册注销功能,以及登录状态验证等功能? flask的扩展库中有Flask-Login库就可快速的实现以上的功能,实现起来是非常的便利。

1. 安装Flask-Login

pip install flask-login

2. 实现登录功能

2.1 定义login.html模板

{% extends 'base.html' %}

{% block title %}
    登录页面
{% endblock %}

{% block content %}
    <form action="" method="post">
        姓名:<input type="text" name="username">
        密码:<input type="text" name="password">
        <input type="submit" value="提交">
    </form>
{% endblock %}

2.2 实现登录功能
登录方法中定义被login_manager.user_loader装饰的回调函数,回调函数在如下两个地方被调用:
1)该函数表明当前用户登录成功时调用login_user()方法时,会被回调的函数。回调函数实现的功能是向会话上下文session中存储最为中间的键值对,key为user_id, value为当前登录用户的ID值。
2)回调函数在访问任何一个路由地址时也会被调用。
注意: 因为请求上下文在每次建立连接时,都需要获取当前登录用户并将当前登录用户设置为全局上下文current_user,因此回调函数返回的是当前登录系统的用户对象。
users->views.py

from flask_login import LoginManager, login_required, login_user, logout_user,current_user

# 获取登录管理对象
login_manager = LoginManager()  

@login_manager.user_loader
def load_user(user_id):
    # 必须编写一个函数用于从数据库加载用户。
    # 这个函数在login_user(user)存储当前登录用户到session中时,会被调用
    # 在每次访问地址的时候都被被调用,用于向请求上下文中绑定当前登录的用户信息
    return User.query.get(user_id)

@blue.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')

    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        # 校验用户名和密码是否填写完成
        if not all([username, password]):
            return render_template('login.html')
        # 通过用户名获取用户对象
        user = User.query.filter_by(username=username).first()
        # 校验密码是否正确
        if check_password_hash(user.password, password):
            # 实现登录
            # login_user()能够将已登录并通过load_user()的用户对应的User对象保存在session中
            # 在session中会创建一个键值对,key为user_id,value为当前登录用户的id值
            # 如果希望应用记住用户的登录状态, 只需要为 login_user()的形参 remember 传入 True 实参就可以了.
            login_user(user)
            return redirect(url_for('user.index'))
        else:
            flash('用户名或者密码错误')

        return redirect(url_for('user.index'))

2.3 启动文件进行配置
session_protection: 设置存储用户登录状态的安全级别
login_view: 设置登录验证失败的跳转地址

from user.views import login_manager

app.config['SECRET_KEY'] = os.urandom(24)

# 登录管理,初始化app
# 可以设置None,'basic','strong'以提供不同的安全等级,一般设置strong,如果发现异常会登出用户
# session_protection 能够更好的防止恶意用户篡改 cookies, 当发现 cookies 被篡改时, 该用户的 session 对象会被立即删除, 导致强制重新登录。
login_manager.session_protection='strong'

# 当登录认证不通过,则跳转到该地址
login_manager.login_view='user.login'
login_manager.init_app(app)

2.4 访问首页,登录校验
使用装饰器login_required()进行登录校验。
核心思想: 校验session中是否存在key为user_id的键值对。如果校验成功,则继续访问被装饰的函数。如果校验失败,则跳转到启动文件中定义的login_manager.login_view定义的视图函数。

@blue.route('/index/')
@login_required
def index():
    return render_template('index.html')

如果登录校验成功,则渲染index.html首页,在页面中可以解析全局变量current_user参数。

{% extends 'base.html' %}

{% block title %}
    首页页面
{% endblock %}

{% block content %}
    <p>我是首页</p>
    <p>当前登录系统用户为: {{ current_user.username }}</p>
{% endblock %}

2.5 注销
使用logout_user()方法实现注销,核心功能就是删除当前会话上下文session中的user_id键值对。
# 退出

@blue.route('/logout/', methods=['GET'])
@login_required
def logout():
    logout_user()
    return redirect(url_for('user.login'))

三、文件上传
1、配置路径
utils->settings.py

import os

# 基础路径
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# static路径
STATIC_DIR=os.path.join(BASE_DIR,'static')

#templates路径
TEMPLATES_DIR=os.path.join(BASE_DIR,'templates')

# 上传路径
UPLOAD_DIR=os.path.join(os.path.join(STATIC_DIR,'media'),'upload')

2、保存文件

       # 获取图片
        icons = request.files.get('icons')
        #保存save(path)
        #绝对路径
        file_path=os.path.join(UPLOAD_DIR,icons.filename)
        #保存图片到绝对路径中
        icons.save(file_path)
        # 保存user对象
        user=current_user
        #将文件保存到数据库中
        user.icons=os.path.join('upload',icons.filename)
        db.session.add(user)
        db.session.commit()

3、html文件

{% extends 'base.html' %}
{% block title %}
首页

{% endblock %}
{% block content %}
首页
<p>当前用户{{current_user.username}}e</p>
<img src="/static/media/{{current_user.icons}}" alt="">
<form action="" method="post" enctype="multipart/form-data">
    头像:<input type="file" name="icons">
    <input type="submit" value="上传">
</form>

{% endblock %}

四、完整的登录注册代码
manager.py

from flask import Flask
from flask_script import Manager

from users.models import db
from users.views import users_blue, login_manager

app = Flask(__name__)

# 注册蓝图
app.register_blueprint(blueprint=users_blue, url_prefix='/users')

# 定义密钥
app.config['SECRET_KEY'] = 'secret_key'

# 配置数据库
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/f_login'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# 没有登录跳转地址(装饰器)
login_manager.login_view='users.user_login'

# 绑定
db.init_app(app)
# 绑定flask-login(登录验证)
login_manager.init_app(app)

# 定义manager管理
manager = Manager(app=app)

if __name__ == '__main__':
    manager.run()

users->views.py

import os

from flask import Blueprint, request, render_template, redirect, url_for
from flask_login import login_user, LoginManager, current_user, login_required, logout_user
from werkzeug.security import generate_password_hash, check_password_hash

from users.forms import UsersRegisterForm, UsersLoginForm

from users.models import db, User
from utils.settings import UPLOAD_DIR

# current_user是全局变量,可以输出user的说有信息

# 定义蓝图
users_blue = Blueprint('users', __name__)

login_manager = LoginManager()


@users_blue.route('/register/', methods=['GET', 'POST'])
def register():
    form = UsersRegisterForm()
    if request.method == 'GET':
        return render_template('register.html', form=form)
    if request.method == 'POST':
        # 验证提交的字段信息
        if form.validate_on_submit():
            username=form.username.data
            password=form.password.data
            #实现注册,保存用户信息到User模型中
            user = User()
            user.username = username
            user.password = generate_password_hash(password)
            db.session.add(user)
            db.session.commit()
            return redirect(url_for('users.user_login'))
        else:
            # 验证失败,form.errors中存在错误信息
            return render_template('register.html', form=form)


@users_blue.route('/create_table/')
def create_table():
    db.create_all()
    return '创建成功'


# 有点类似中间件
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(user_id)


@users_blue.route('/login/', methods=['GET', 'POST'])
def login():
    form = UsersLoginForm()
    if request.method == 'GET':
        return render_template('login.html', form=form)
    if request.method == 'POST':
        if form.validate_on_submit():
            username=form.username.data
            password=form.password.data
            user = User.query.filter(User.username == username).first()
            # 校验用户名和密码是否填写完整
            if user:
                # 获取用户,进行密码判断
                if check_password_hash(user.password,password):
                    #密码正确
                    #实现功能
                    login_user(user)
                    return redirect(url_for('users.index'))
                else:
                    error_password = '密码错误'
                    return render_template('login.html', error_password=error_password,form=form)
            else:
                # 获取不到用户,返回错误信息给页面
                error_username = '该用户没有注册,请去注册'
                return render_template('login.html',error_username=error_username,form=form)
        else:
            return render_template('login.html', form=form)


# @users_blue.route('/user_login/', methods=['GET', 'POST'])
# def user_login():
#     if request.method == 'GET':
#         return render_template('login.html')
#     if request.method == 'POST':
#         username = request.form.get('username')
#         password = request.form.get('password')
#         user = User.query.filter(User.username == username).first()
#         # 校验用户名和密码是否填写完整
#         if not all([username,password]):
#             return render_template('login.html')
#         if user:
#             # 获取用户,进行密码判断
#             if check_password_hash(user.password,password):
#                 #密码正确
#                 #实现功能
#                 login_user(user)
#                 return redirect(url_for('users.index'))
#         else:
#             # 获取不到用户,返回错误信息给页面
#             error = '该用户没有注册,请去注册'
#             return render_template('login.html',error=error)


@users_blue.route('/index/',methods=['GET', 'POST'])
@login_required
def index():
    if request.method == 'GET':
        return render_template('index.html')
    if request.method == 'POST':
        # 获取图片
        icons = request.files.get('icons')
        #保存save(path)
        # 作业:只保存jpg,png。。。 实现中间件,
        # @user_blueprint.before_request
        # @user_blueprint.after_request
        # @user_blueprint.teardown_request
        file_path=os.path.join(UPLOAD_DIR,icons.filename)
        icons.save(file_path)
        # 保存user对象
        user=current_user
        user.icons=os.path.join('upload',icons.filename)
        db.session.add(user)
        db.session.commit()
        return redirect(url_for('users.index'))


@users_blue.route('/logout/')
@login_required
def logout():
    logout_user()
    return redirect(url_for('users.user_login'))

users->models.py

from flask_login import UserMixin
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


class User(UserMixin,db.Model):
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    username = db.Column(db.String(20),unique=True,nullable=False)
    password = db.Column(db.String(130),nullable=False)
    icons = db.Column(db.String(100),nullable=True)

    __tablename__='user'

users->forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, EqualTo, ValidationError

from users.models import User


class UsersRegisterForm(FlaskForm):
    # 定义用户名和密码都是必填项
    username = StringField('姓 名', validators=[DataRequired()])
    password = PasswordField('密 码', validators=[DataRequired()])
    password2 = PasswordField('确认密码',
                              validators=[DataRequired(), EqualTo('password', '密码不一致')])
    submit = SubmitField('注册')

    def validate_username(self, field):
        user = User.query.filter(User.username == field.data).first()
        if len(field.data) < 3:
            raise ValidationError('用户名不能少于3个字符')
        if len(field.data) > 20:
            raise ValidationError('用户名不能大于20个字符')
        if user:
            raise ValidationError('用户名已存在')

    def validate_password(self, field):
        if len(field.data) < 6:
            raise ValidationError('密码不能少于6个字符')

    def validate_password2(self, field):
        if len(field.data) < 6:
            raise ValidationError('确认密码不能少于6个字符')


class UsersLoginForm(FlaskForm):
    username=StringField('用户名',validators=[DataRequired()])
    password = PasswordField('密码', validators=[DataRequired()])
    submit=SubmitField('登录')

    def validate_username(self, field):
        if len(field.data) < 3:
            raise ValidationError('用户名不能少于3个字符')
        if len(field.data) > 20:
            raise ValidationError('用户名不能大于20个字符')

    def validate_password(self, field):
        if len(field.data) < 6:
            raise ValidationError('密码不能少于6个字符')

utils->settings.py

import os

# 基础路径
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# static路径
STATIC_DIR=os.path.join(BASE_DIR,'static')

#templates路径
TEMPLATES_DIR=os.path.join(BASE_DIR,'templates')

# 上传路径
UPLOAD_DIR=os.path.join(os.path.join(STATIC_DIR,'media'),'upload')

templates->base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>
    {% block css %}
    {% endblock %}

    {% block js %}
    {% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>

templates->base_main.html

{% extends 'base.html' %}
{% block js %}
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"type="text/javascript"></script>
{% endblock %}

templates->register.html

{% extends 'base_main.html' %}
{% block title %}
注册
{% endblock %}
{% block content %}
<form action="" method="post">
    {{ form.csrf_token }}

    {% if form.errors.username %}
        {{ form.errors.username.0 }}
    {% endif %}
    <p>{{ form.username.label }}: {{ form.username }}</p>
    {% if form.errors.password %}
        {{ form.errors.password.0 }}
    {% endif %}
    <p>{{ form.password.label }}: {{ form.password }}</p>
    {% if form.errors.password2 %}
        {{ form.errors.password2.0 }}
    {% endif %}
    <p>{{ form.password2.label }}: {{ form.password2 }}</p>
    {{ form.submit() }}
</form>

{% endblock %}

templates->login.html

{% extends 'base_main.html' %}
{% block title %}
登录
{% endblock %}
{% block content %}
<form action="" method="post">
    {{ form.csrf_token }}

    {% if form.errors.username %}
    {{ form.errors.username.0 }}
    {% endif %}

    {% if error_username %}
    {{ error_username }}
    {% endif %}

    <p>{{ form.username.label }}: {{ form.username }}</p>

    {% if form.errors.password %}
    {{ form.errors.password.0 }}
    {% endif %}

    {% if error_password %}
    {{ error_password }}
    {% endif %}

    <p>{{ form.password.label }}:{{ form.password }}</p>

    {{ form.submit() }}
    <!--<p>姓名:<input type="text" name="username"></p>-->
    <!--<p>密码:<input type="password" name="password"></p>-->
    <!--<input type="submit" value="登录">-->
</form>

{% endblock %}

templates->index.html

{% extends 'base.html' %}
{% block title %}
首页

{% endblock %}
{% block content %}
首页
<p>当前用户{{current_user.username}}e</p>
<img src="/static/media/{{current_user.icons}}" alt="">
<form action="" method="post" enctype="multipart/form-data">
    头像:<input type="file" name="icons">
    <input type="submit" value="上传">
</form>

{% endblock %}

作者:晓晓的忍儿
链接:https://www.jianshu.com/p/9e343d10f7a2
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。