apache+svn代码直接发布系统-阿里云开发者社区

开发者社区> 技术小胖子> 正文

apache+svn代码直接发布系统

简介:
+关注继续查看

问题1:发布代码这种事,开发嫌慢,运维嫌烦,策划和测试狂催,如果开发直接提交svn后线上代码也是最新的就大家都不用烦了.

问题2:小公司电脑少,svn只有一个,永远不够用啊,各种文件和代码堆堆堆,时间一长就连自己也不知道放哪里了,如果一台机有多个svn目录独立控制,那就简单了.

  我相信上面两个问题在大部分公司都存在,代码发布和文件管理是任何公司都有的核心功能,当然了,有的公司技术强大,用高大上git必须比svn好用,不过大部分还是svn为主,要解决上面两个问题,那就看下面了.

  先来看看原理图:

    问题一的解决方案

wKioL1fXn9uy2Oi7AABpWKFTQDI015.png-wh_50

    问题的关键就在钩子脚本,一会看一下详细的.

    问题二的解决方案

wKiom1fXoWOCdvBaAACUjpuXDP0943.png-wh_50

    问题的关键就是利用apache多域控制多端口,不同的端口指向不同的svn数据库,这样就可以做到单机多svn目录.


下面来说怎么安装和配置整套应用


1.安装应用

    说到安装方法,那当然是yum最简单,呵呵~!我也就不纠结太多了,纠结编译安装的,可以看着下面的软件逐个去弄,这真的不是问题,编译参数我在附录贴一下.

    顺便说说,我是在centos7.2安装的,所以按道理是通用的了.

1
2
3
4
5
6
7
8
9
10
11
12
13
#安装依赖包
yum install -y Python pyOpenSSL zlib* apr* swig* neon*
#安装libiconv,这个yum不了,只能编译,但是也很简单就是了
wget "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz"
tar xf libiconv-1.14.tar.gz
cd libiconv-1.14
./configure --prefix=/usr/local/libiconv
make
make install 
#安装apache服务  
yum install -y httpd  
#安装svn服务和httpd模块  
yum install -y subversion mod_dav_svn

    这个就安装完了,如果硬要编译的话,嗯~!那可是一长串,我表示不太喜欢,反正这些需求都是内部的,能用就行,性能都不是重点.

  

2.配置apache和关联svn

    理论上apache安装完就能用,但是这个并不是重点,他和svn两者要配合使用,那必然是有关联的地方,而因为是yum安装的,http的conf目录在/etc下面,

先配置apache的基本配置,

1
2
3
4
5
6
7
8
9
10
#创建运行用户,注意不要创建/sbin/nologin的用户使用在apache+svn上,因为会导致钩子脚本无法使用
groupadd www
useradd www -g www
#修改httpd.conf,里面东西太多,只说几个重点
vim /etc/httpd/conf/httpd.conf
#配置端口,默认80,你喜欢改什么都可以,不冲突就行
Listen 8001
#运行用户和用户组,你创什么就用什么,要保证是可以运行脚本的权限/bin/bash
User www
Group www

    然后其他默认就可以了.

然后配置两者关联的相关配置配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#配置subversion.conf文件如下内容
vim /etc/httpd/conf.modules.d/10-subversion.conf
LoadModule dav_svn_module     modules/mod_dav_svn.so
LoadModule authz_svn_module   modules/mod_authz_svn.so
LoadModule dontdothat_module  modules/mod_dontdothat.so
<Location /svn>
    Dav svn
    SVNListParentPath on
#----允许在网页上显示svn父目录list --记住,注释不要和配置项写到同一行,否则会出错..
    SVNParentPath "/data/svn"
    AuthType Basic
#----连接框提示
    AuthName "Subversion Repository"
#----用户密码配置文件
    AuthUserFile /etc/svn/passwd
#----用户权限配置文件
    AuthzSVNAccessFile /etc/svn/authz
#----限制登录配置,可细化到只允许那个IP登录,这里注释了,暂时不使用
#    Order Allow,Deny
#    Allow from 127.0.0.1
#   Allow from {任何IP类型}
#    Deny from All
    Require valid-user
</Location>
 
#可以再加一个Location来实现多svn仓库,类似这样
#<Location /svn2>
#中间省略...
#</Location>

再然后修改一些权限的问题和启动apache

1
2
3
4
5
6
7
#修改数据仓库目录/data/svn权限(后面用到) 
chown www:www -R /data/svn
chmod 777 -R /data/svn
#关闭SELinux  
setenforce 0  
#重启apache服务  
service httpd restart

    这样apache部分就完毕了


3.创建svn数据仓库

    首先要强调,svn的数据仓库和我们认知的checkout出来的svn文件目录不同,svn的数据仓库他是经过编码的二进制文件,不能直接解析,和我们能直接操作的svn文件夹是两回事,我们提交和下载的文件都是经过编码解析的,大家心里要又这个概念.

    下面来看操作

1
2
3
4
5
6
7
8
9
#创建仓库目录  
mkdir -p /data/svn/test_1 
#创建svn仓库  
svnadmin create /data/svn/test_1  
#配置改仓库的配置文件  
vim /data/svn/test_1/conf/svnserve.conf  
#禁用匿名用户并开启验证用户权限。  
anon-access = none  
auth-access = write


4.创建svn用户

    还记得关联svn时apache配置的用户认证相关目录么,没错,是/etc/svn,另外,因为用了apache,所以用户的密码不再是传统svn的明文保存,需要通过htpasswd来加密配置,所以要独立开来讲讲

1
2
3
4
5
6
7
8
#进入目录
cd /etc/svn
#使用htpasswd创建用户,首次创建用户会建立新文件,删除旧文件,前面是用户,后面是密码 
htpasswd -cb passwd test 123  
#添加/修改用户  
htpasswd -b passwd new-user new-password  
#删除用户  
htpasswd -D passwd new-user

    然后相关的用户名和密码就会加密保存在/etc/svn/passwd里面,有兴趣的可以看一下文件内容,是密文存储的.


5.配置svn权限

    和密码认证文件一样的位置,主要要注意的地方是他分为组权限,不同的组可以有不同的权限,没规定什么名字,可以细化到目录,其实也就只分读和写两个权限,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#编辑authz文件,设置guest组包含test和abc两个用户,devl组aaa和bbb两个用户。
vim /etc/svn/authz
[groups]
guest = test,abc
devl = aaa,bbb
#配置组权限,设置根目录下guest组为可读写,其他用户为可读。
[/]
@guest = rw
* = r
#设置test_1目录下devl组为可读,其他没权限
[test_1:/]
@g_developer = r
* =
#设置test_1目录下tags文件夹devl组为可读写,其他没权限
[test_1:/tags]
@g_developer = rw
* =

    权限配置就这么完毕了,然后,并不需要启动svn程序,就可以使用的了,因为apache会调用插件,不需要svn程序额外启动,相当方便,减少一个程序占用内存.


6.创建钩子脚本

    关于钩子脚本的用途,就是说在提交/更新/删除各种场景发生前后执行的脚本的意思,是svn自带的功能,非常方便,这也是自动发布代码的关键所在.

    svn也自带一些模板,就在数据仓库目录里面

1
2
3
4
5
6
7
8
9
10
ll /data/svn/test_1/hooks/
-rw-r--r-- 1 www www 1977 9月  13 10:33 post-commit.tmpl
-rw-r--r-- 1 www www 1638 9月  13 10:33 post-lock.tmpl
-rw-r--r-- 1 www www 2289 9月  13 10:33 post-revprop-change.tmpl
-rw-r--r-- 1 www www 1567 9月  13 10:33 post-unlock.tmpl
-rw-r--r-- 1 www www 3426 9月  13 10:33 pre-commit.tmpl
-rw-r--r-- 1 www www 2434 9月  13 10:33 pre-lock.tmpl
-rw-r--r-- 1 www www 2786 9月  13 10:33 pre-revprop-change.tmpl
-rw-r--r-- 1 www www 2122 9月  13 10:33 pre-unlock.tmpl
-rw-r--r-- 1 www www 2780 9月  13 10:33 start-commit.tmpl

    有心研究的可以慢慢研究,下面这个就是提交后执行的那个脚本post-commit,只要有提交动做,这个脚本就会执行.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#创建或修改post-commit,成功提交后执行的钩子
vim /data/svn/test_1/hooks/post-commit
#!/bin/bash
SVN=/usr/bin/svn
LOG=/var/log/svnup-houtai.log
wwwurl=/data/htdocs/
export LANG=zh_CN.UTF-8
export PATH
cat $LOG >> ${LOG}.bak
echo ""     > $LOG
echo `date` >> $LOG
echo "##############################" >> $LOG
cd ${wwwurl};$SVN co --username test --password '123' http://192.168.1.86:8001/svn/test_1 www >> $LOG
echo "##############################" >> $LOG
wait
=========================================

 升级版钩子脚本,远程checkout,要预先使用ssh-keygen做好免密码登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
SVN=/usr/bin/svn
LOG=/var/log/svnup-newips.log
export LANG=zh_CN.UTF-8
export PATH
cat $LOG >> ${LOG}.bak
echo ""     > $LOG
echo `date` >> $LOG
echo "##############################" >> $LOG
commend="$SVN co --username test --password '123' http://192.168.1.86:8001/svn/newips /data/htdocs/www "
auto_scp() {
ip=10.29.66.55
PORT=22
expect<<EOF
set timeout 1200
spawn ssh -q -o StrictHostKeyChecking=no ${ip} -l root -p ${PORT}
expect -- "*~]#*"
send -- "${commend}\r"
expect -- "*~]#*"
send -- "exit\r"
expect eof
EOF
}
auto_scp >>$LOG
echo "##############################" >> $LOG
wait

===========================================

重点解析下,注意空格分割

1
SVN co --username test --password '123' http://192.168.1.86:8001/svn/test www

    svn是命令,co是更新数据的意思,username就是用户,password就是密码,http后面那串就是svn地址,最后的www意思是创建并更新到什么目录的意思,例如我的代码是放在/data/htdocs的www目录,如果有就更新,如果没就创建了再更新.

    所以这句的意思就是把svn仓库的文件更新到/data/htdocs/www代码目录里面,你的代码目录如果叫app,那最后的www就可以改成app.


    而这个目录要是一开始就配置成nginx等web系统的php或者java代码目录的话,那么只要你提交svn代码,通过钩子脚本的运行,你的代码目录也会同时更新,也就是我开头说的---代码直接发布系统.


    然后还没完,因为这个是要apache执行的,而apache的执行用户是www,所以要做脚本运行就要操作这一步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#设置post-commit执行权限,不然会报错的
chmod 777 /data/svn/test_1/hooks/post-commit
#切换用户执行下脚本,如果不执行,可能钩子也是不生效的,因为有应答提醒
su - www /data/svn/test_1/hooks/post-commit
上一次登录:二 9月 13 12:01:08 CST 2016pts/0 
 
-----------------------------------------------------------------------
注意!  你的密码,对于认证域:
 
   <http://192.168.1.86:8001> Subversion Repository
 
只能明文保存在磁盘上!  如果可能的话,请考虑配置你的系统,让 Subversion
可以保存加密后的密码。请参阅文档以获得详细信息。
 
你可以通过在“/home/www/.subversion/servers”中设置选项“store-plaintext-passwords”为“yes”或“no”,
来避免再次出现此警告。
-----------------------------------------------------------------------
保存未加密的密码(yes/no)?yes
ll /data/svn/test_1/hooks/post-commit
总用量 40
-rwxrwxrwx 1 www www  416 9月  13 12:07 post-commit

    然后,你的就可以用了

    有些时候,我们的svn要求必须有做注释,方便翻(zhao)查(ren)数(bei)据(guo),所以就有了第二个脚本,强制提交时输入注释:    

1
2
3
4
5
6
7
8
9
10
11
12
13
#这个是要求在提交时触发的钩子,当然就不是原本那个文件了
vim /data/svn/test_1/hooks/pre-commit
#!/bin/bash
REPOS="$1"
TXN="$2"
SVNLOOK=/usr/bin/svnlook
LOGMSG=$($SVNLOOK log -t "$TXN" "$REPOS" grep "[a-zA-Z0-9]" wc -c)
if "$LOGMSG" -lt 3 ]; then
echo -e "\n 提交文件时必须添加注释,提交中止."1>&2
exit 1
fi
#别忘记切换用户执行以下,和上面一样操作就可以了,
su - www /data/svn/test_1/hooks/pre-commit

其中注意,

LOGMSG=$($SVNLOOK log -t "$TXN" "$REPOS" | grep "[a-zA-Z0-9]" | wc -c)

是统计这个注释有多少个字母和数字的字符总数量的意思.中文不能被统计,所以要注意注释的格式,

if [ "$LOGMSG" -lt 3 ]; then

是限制输入字数的意思,这里是至少3个字符的意思可以更多,也可以更少,大家看自己偏好.


7.测试

    其实钩子脚本里面也有地址测试,不过也可以自己测试,无论是windows还是linux都用这个地址http://192.168.1.86:8001/svn/test_1

1
svn co http://192.168.1.86:8001/svn/test_1 /data/htdocs/www --username=test --password=123

   windows的怎么测我就不多说了,就是界面而已.


8.备份/还原svn数据仓库

    这个算是个基本需求吧,不过我觉得一定情况来说其实有点鸡肋,一般来说svn都是很多人一起用,每个人电脑里面的文件就是一个备份,还特地去备份和还原就有点多此一举的感觉,除非说这个svn数据非常多,重新上传超级费时间,或者说非常在意svn存放的注释记录和版本记录,那倒是说得过去,上面纯属个人简介,下面转回正题.

    备份的方式现在有三种,其实有很多同类文章了,我也就不想详细介绍了,

1)svnadmin dump 
2)svnadmin hotcopy 
3)svnsync.

第三种除了特定场景几乎没人用,也很少例子,第二种在以前资源紧张的旧服务器倒是有优势,现在也是没什么优势了,先看第一种.

1
2
3
4
5
6
7
8
9
#第一种方法备份命令,详细参数请看下面
svnadmin dump /data/svn/[你的svn目录] --deltas  > test-svn-7.dump 2>/dev/null
#参数说明:
-r [--revision]:    指定备份的版本号(或X:Y范围)
--incremental:    以增量方式进行转存
--deltas:    使用压缩,消耗更多cpu资源
-q [--quiet]:    在标准错误输出不显示进度(仅错误)
#备份完了还想加大压缩比,那就在tar一下吧,
tar zcf test-svn-7.tar.gz test-svn-7.dump

然后来看第二种

1
2
3
4
#第二种方法备份命令,其实也等于直接拷贝
svnadmin hotcopy /data/svn/[你的svn目录] /root/[目的备份svn目录]
#这个文件夹肯定是和原本的一样大,所以你更加需要压缩
tar zcf test-svn-7.tar.gz /root/test-svn

然后,就看怎么恢复了,第二种方式就不用说了,都是一个完整目录了,直接改配置文件或者复制到相关http目录就完全能用起来了,主要来看第一种,

1
2
3
4
5
6
#第一种方式恢复命令,先创建一个新的版本库
svnadmin   create  /svn/project/test1   
#先还原完全备份,
svnadmin load /svn/project/test1 test-svn-7.dump  
#如果有做增量备份,再还原增量备份的内容    
svnadmin load /svn/project/test1 < svn.bak.1

这样就完成了备份和恢复了.


9.搭建用户密码自助修改功能

    如果大家用起来,就会发现这个svn的账户密码,必须管理员来添加和修改.其实不算很方便,然而创建密码用的工具htpasswd本身是具有加密功能的,单纯用脚本调用修改又变得很麻烦,网上很多同类的文章也都是忽略了加密算法问题,造成实现不了的情况,我也是翻查了很多资料,然后分析了脚本得出这个结果.

    那要怎么办呢?经过分析和权衡利弊,总算找到一个姑且能算是解决的方法,不过有一个大坑,而这个坑的解决方案,就得看有没有高手去破解算法了,我暂时是没有办法的了.

    好了,转回正题,我们用的方法,其实就是cgi方法的变种,网上一搜一大堆的ChangePasswd.cgi,不过如果你按他们的方法做,一般情况下是失败的,也就是因为我说的算法问题,这个cgi的perl脚本里的crypt函数的算法和htpasswd的算法是不一样的,所以无论你怎么改,都会提示旧密码错误.不过从另一方面来说也有好的一面,密码那么容易被破解,那这个加密算法还真是失败透了.

    先来看ChangePasswd.ini,只是个配置文件了,记得给读的权限啊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#打开
vim ChangePasswd.ini
[path]
authuserfile=/etc/svn/passwd
logfile=/var/www/cgi-bin/ChangePasswd.log
[setup]
pwdminlen=6
[html]
title=SVN用户密码自助修改
description=SVN用户密码自助修改
yourname=用户名
oldpwd=旧密码
newpwd1=新密码
newpwd2=确认新密码
btn_change=修 改
btn_reset=重 置
changepwdok=成功修改密码
changepwdfailed=修改密码失败
servererror=服务器错误
passmustgreater=新密码位数必须大于
twopassnotmatched=两密码不一致
entername=请输入用户名
enterpwd=密码未输入
errorpwd=你的密码不正确
back=返回

需要改的只有两个地方,authuserfile就是你svn密码存放文件,logfile就是你日志的文件了,通常你保证能读写就好了.

再看重点的ChangePasswd.cgi,这个就是执行文件,要给执行权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/usr/bin/perl -w
use strict;
use CGI;
my $time        localtime;
my $remote_id   $ENV{REMOTE_HOST} || $ENV{REMOTE_ADDR};
my $admin_email $ENV{SERVER_ADMIN};
my $cgi = new CGI;
my $pwd_not_alldiginal "密码不能全为数字";
my $pwd_not_allchar "密码不能全为字符";
my $user_not_exists ="该用户不存在";
my $file_not_found ="文件不存在,请联系管理员";
my $authuserfile;
my $logfile;
my $pwdminlen;
my $title;
my $description;
my $yourname;
my $oldpwd;
my $newpwd1;
my $newpwd2;
my $btn_change;
my $btn_reset;
my $changepwdok;
my $changepwdfailed;
my $oldpwderror;
my $passmustgreater;
my $twopassnotmatched;
my $entername;
my $enterpwd;
my $errorpwd;
my $back;
&IniInfo;
 
if ($cgi -> param())
{#8
my $User $cgi->param('UserName');
my $UserPwd $cgi->param('OldPwd');
my $UserNewPwd $cgi->param('NewPwd1');
my $MatchNewPwd $cgi->param('NewPwd2');
if (!$User)
     {&Writer_Log("Enter no user name");
       &otherhtml($title,$entername,$back);}
elsif (!$UserPwd )
    {&Writer_Log("Enter no OldPasswd");
     &otherhtml($title,$enterpwd,$back); }
elsif (length($UserNewPwd)<$pwdminlen)
    {&Writer_Log("Password's length must greater than".$pwdminlen);
     &otherhtml($title,$passmustgreater.$pwdminlen,$back);}
elsif ($UserNewPwd =~/^\d+$/)
    {&Writer_Log("New Passwd isn't all diginal");
     &otherhtml($title,$pwd_not_alldiginal,$back);}
elsif ($UserNewPwd =~/^[A-Za-z]+$/)
    {&Writer_Log("New Passwd isn't all char");
     &otherhtml($title,$pwd_not_allchar,$back);}
elsif ($UserNewPwd ne $MatchNewPwd)
    {&Writer_Log("Two new passwords are not matched");
     &otherhtml($title,$twopassnotmatched,$back);}
else
{if($authuserfile)
{#6
open UserFile, "<$authuserfile" or die "打开文件失败:$!";
while (<UserFile>)
    {#5
       my $varstr=$_;
       if($varstr =~/($User)/)
    {#3
     my $eqpos =index($varstr":");
     my $UserName substr($varstr,0,$eqpos);
     my $cryptpwd substr($varstr,$eqpos + 1,13);
     next if($UserName ne $User);
#     if(crypt($UserPwd,$cryptpwd) eq $cryptpwd)
      if(crypt($UserPwd,$cryptpwd) ne $cryptpwd)
     {#a
      my $rc system("/usr/bin/htpasswd -b $authuserfile $User $UserNewPwd");
      if ($rc == 0)
         {#1
            &Writer_Log$User.":Change Passwd");
            &otherhtml($title,$changepwdok,$back);
          }#1
       else
          {#2
           &Writer_Log$User.":Change Passwd Failed");
           &otherhtml($title,$changepwdfailed,$back);
          }#2
       exit;
     }#a
     else
     {#b
      &Writer_Log("Old Passwd is Incorrect ");
      &otherhtml($title,$errorpwd,$back);
     }#b
     exit;      
    }#3
       else
    {#4
     if(eof)
     &Writer_Log($User.":no this user");
       &otherhtml($title,$user_not_exists,$back);
       exit;
     }
     else
     {next;}
    }#4  
     }#5
   close UserFile;
}#6
else
{#7
   &Writer_Log($authuserfile.":no found");
   &otherhtml($title,$file_not_found,$back);
}#7
}
}#8
else
{&Index_Html;}
 
sub IniInfo{
my $inifile "/var/www/cgi-bin/ChangePasswd.ini";
open CGI_INI_FILE, "<$inifile" or die "打开文件失败:$!";;
while (<CGI_INI_FILE>)
{
my $eqpos =index($_,'=');
my $len length($_);
if ($_ =~/authuserfile/)
{$authuserfilesubstr($_$eqpos + 1, $len $eqpos -2);}
elsif ($_ =~/logfile/)
{$logfilesubstr($_$eqpos + 1);}
elsif ($_ =~/pwdminlen/)
{$pwdminlensubstr($_$eqpos + 1);}
elsif ($_ =~/title/)
{$title substr($_$eqpos + 1);}
elsif ($_ =~/description/)
{$description substr($_$eqpos + 1);}
elsif ($_ =~/yourname/)
{$yourname substr($_$eqpos + 1);}
elsif ($_ =~/oldpwd/)
{$oldpwdsubstr($_$eqpos + 1);}
elsif ($_ =~/newpwd1/)
{$newpwd1substr($_$eqpos + 1);}
elsif ($_ =~/newpwd2/)
{$newpwd2substr($_$eqpos + 1);}
elsif ($_ =~/btn_change/)
{$btn_change substr($_$eqpos + 1);}
elsif ($_ =~/btn_reset/)
{$btn_reset substr($_$eqpos + 1);}
elsif ($_ =~/changepwdok/)
{$changepwdok substr($_$eqpos + 1);}
elsif ($_ =~/changepwdfailed/)
{$changepwdfailed substr($_$eqpos + 1);}
elsif ($_ =~/oldpwderror/)
{$oldpwderror substr($_$eqpos + 1);}
elsif ($_ =~/passmustgreater/)
{$passmustgreater substr($_$eqpos + 1);}
elsif ($_ =~/twopassnotmatched/)
{$twopassnotmatched substr($_$eqpos + 1);}
elsif ($_ =~/entername/)
{$entername substr($_$eqpos + 1);}
elsif ($_ =~/enterpwd/)
{$enterpwdsubstr($_$eqpos + 1);}
elsif ($_ =~/errorpwd/)
{$errorpwdsubstr($_$eqpos + 1);}
elsif ($_ =~/back/)
{$back substr($_$eqpos + 1);}
}
close CGI_INI_FILE;
}
 
sub Index_Html{
print "Content-type: text/html\n\n";
print <<END_OF_PAGE;
<html >
<head>
<title>$title</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<HR>
<center><h1>$description</h1>
</center>
<form method="POST" enctype="multipart/form-data" action="/cgi-bin/ChangePasswd.cgi">
<br>
<TABLE align="center">
<TR><TD class="t_text">$yourname</TD><TD><input type="text" name="UserName" /></TD></TR>
<TR><TD class="t_text">$oldpwd</TD><TD><input type="password" name="OldPwd" /></TD></TR>
<TR><TD class="t_text">$newpwd1</TD><TD><input type="password" name="NewPwd1" /></TD></TR>
<TR><TD class="t_text">$newpwd2</TD><TD><input type="password" name="NewPwd2" /></TD></TR>
</TABLE>
<br>
<TABLE align="center">
<TR><TD><input type="submit" name="chgpasswd" value="$btn_change"> <input type="reset" value="$btn_reset"></TD></TR>
</TABLE>
</form>
<HR>
<font color="#FF0000">注意:新密码位数必需大于$pwdminlen,且为字母与数字组合</font>
</body>
</html>
END_OF_PAGE
}
 
sub otherhtml{
print "Content-type: text/html\n\n";
print <<END_OF_PAGE;
<html>
<head>
<meta http-equiv="Content-Language" content="zh-cn">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>$_[0]</title>
</head>
<body>
<p align="center"><font size="5">$_[1]</font></p>
<p align="center"><a href="/cgi-bin/ChangePasswd.cgi"><font size="4">$_[2]</font></a></p>
<HR>
</body>
</html>
END_OF_PAGE
}
 
sub Writer_Log{
if($logfile)
{
my $loginfo ="[".$time."] "." [".$remote_id."] "." || ".$_[0];
open LOGFILE,">>$logfile" or die "Couldn't open LOG FILE for writing: $!";
print LOGFILE ("$loginfo\n");
close LOGFILE;
}
}

这个脚本其实和网上的相差不大,只是我改了一个地方,把本来不成立的判断变成了成立,所以就没了我开始说的算法不一致导致不能用的问题.重点就在这里:

1
2
#     if(crypt($UserPwd,$cryptpwd) eq $cryptpwd)
      if(crypt($UserPwd,$cryptpwd) ne $cryptpwd)

原本是求crypt函数结果和用htpasswd加密的结果相等,那才会更改密码,但是事实上两者是恒定不等的,因为算法不一样,所以就造成永远用不了的问题,后来我想,既然他们不相等,那就干脆把相等的条件改成不等吧.不过,隐患还是很明显,这样做的话,就算乱输一个密码,也是能把密码改掉,这样做就很危险了,其他人只要知道账号,那就随便把密码改了,所以说是一个大坑了,期待有大牛能解决这个问题.

    虽然伴随着大坑的到来,但是功能终究是能实现的,只要控制得当,限制修改密码的时间或者配置apache的条件,我觉得还是有搞头的,所以就这么上马了.

    好了,然后继续下面的步骤,把这两个文件,放到apache的默认cgi代码目录,并创建刚才的log文件

1
2
3
4
5
6
7
8
9
10
#放到这个目录
ll /var/www/cgi-bin/
total 24
-rwxrwxrwx 1 www  www  6342 Mar 24 19:24 ChangePasswd.cgi*
-rwxrwxrwx 1 www  www   566 Mar 24 17:54 ChangePasswd.ini*
-rwxrwxrwx 1 www  www   365 Mar 25 10:49 ChangePasswd.log*
#因为是perl脚本,还需要安装一个perl插件,不然可能报错
yum install -y perl-CGI
#重载apache
service apache2 reload

一般来说,如果你是用centos的yum安装的话,就直接能用了,默认是加载cgi插件的,不过可能没有perl的插件而已,yum一下也是可以的了.

不过如果你是ubuntu或者其他安装的话,apache未必是加载cgi插件的,所以就要多下面这些步骤了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#假设是ubuntu,也是先安装perl插件,不过名字不一样
apt-get install -y libapache2-mod-perl2
#然后,创建一个连接,并且让他加载cgi.pl
vim /etc/apache2/mods-enabled/alias.conf
Alias /cgi-bin/ "/var/www/cgi-bin/"
        <Directory "/var/www/cgi-bin/">
            AddType application/x-httpd-php .php
            AddHandler   cgi-script   .pl
            AddHandler   cgi-script   .cgi
            Options ExecCGI
            DirectoryIndex index.html
            AllowOverride none
            Order allow,deny
            Allow from all
        </Directory>
#重载apache
service apache2 reload

然后,就可以用了,访问地址

http://'svn地址'/cgi-bin/ChangePasswd.cgi

就能出现界面了





     本文转自arthur376 51CTO博客,原文链接:http://blog.51cto.com/arthur376/1852369,如需转载请自行联系原作者






版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Apache Spark Delta Lake 写数据使用及实现原理代码解析
Apache Spark Delta Lake 写数据使用及实现原理代码解析 Delta Lake 写数据是其最基本的功能,而且其使用和现有的 Spark 写 Parquet 文件基本一致,在介绍 Delta Lake 实现原理之前先来看看如何使用它,具体使用如下: df.
4226 0
Linux文件系统—源代码导读
  众所周知,文件系统是Unix系统最基本的资源。最初的Unix系统一般都只支持一种单一类型的文件系统,在这种情况下,文件系统的结构深入到整个系统内核中。而现在的系统大多都在系统内核和文件系统之间提供一个标准的接口,这样不同文件结构之间的数据可以十分方便地交换。
891 0
Apache Dubbo 被曝出“高危”远程代码执行漏洞
6 月 23 日,360 网络安全响应中心(360CERT)发布《CVE-2020-1948:Apache Dubbo 远程代码执行漏洞通告》(以下简称《通告》)。《通告》称,Apache Dubbo 存在远程代码执行漏洞,受影响的版本有 Dubbo 2.5.x、Dubbo 2.6.0 - 2.6.7 和 Dubbo 2.7.0 - 2.7.6。
724 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
12034 0
git学习------&gt;从SVN迁移到Git之后,项目开发代码继续在SVN提交,如何同步迁移之后继续在SVN提交的代码到Git?
最近逐步逐步的将公司的项目都从SVN往Git迁移了,但是想团队成员都能够一步到位就迁移到Git是不可能的,因为还有大部分人都还不会Git,所以整个过渡过程估计得大半年。
1596 0
创建SinaSAE云账号创建和发布基于SVN代码管理的PHP空工程
1、使用Sina微博账号登录,进入【新浪云控制台】 2、选择【云应用SAE】界面,选择【创建新应用】 3、点击【继续创建】,进入应用信息编辑界面     依次编辑“二级域名”“应用程序名”,再点【运行环境】中的【PHP空应用】,再点左下角的【创建应用】完成应用创建。
846 0
21119
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载