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/)
{ $authuserfile substr ( $_ $eqpos  + 1,  $len  $eqpos  -2);}
elsif  ( $_  =~/logfile/)
{ $logfile substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/pwdminlen/)
{ $pwdminlen substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/title/)
{ $title  substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/description/)
{ $description  substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/yourname/)
{ $yourname  substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/oldpwd/)
{ $oldpwd substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/newpwd1/)
{ $newpwd1 substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/newpwd2/)
{ $newpwd2 substr ( $_ $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/)
{ $enterpwd substr ( $_ $eqpos  + 1);}
elsif  ( $_  =~/errorpwd/)
{ $errorpwd substr ( $_ $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,如需转载请自行联系原作者






相关文章
|
1月前
|
消息中间件 存储 大数据
Apache Kafka: 强大消息队列系统的介绍与使用
Apache Kafka: 强大消息队列系统的介绍与使用
|
3月前
|
Ubuntu 安全 网络安全
百度搜索:蓝易云【Ubuntu系统SVN服务器搭建教程】
现在,你已经成功在Ubuntu系统上搭建了SVN服务器。其他用户可以通过SVN客户端连接到你的SVN服务器,进行代码版本管理和协作开发。注意,为了安全起见,建议配置SSL加密以保护数据传输。
39 1
|
6月前
|
Linux 网络安全 Apache
百度搜索:蓝易云 ,linux系统 Apache服务配置教程。
现在,您可以通过浏览器访问您的网站。在浏览器中输入服务器的IP地址或域名,即可访问默认网站或配置的虚拟主机。以上是在Linux系统上配置Apache服务的教程。请根据您的需求进行必要的配置和修改。
83 0
|
7月前
|
IDE Linux 开发工具
从旧服务器迁移svn到另一台新服务器中(linux系统)|遇到诸多坑,已解决
从旧服务器迁移svn到另一台新服务器中(linux系统)|遇到诸多坑,已解决
|
7月前
|
SQL 分布式计算 Java
Apache IoTDB开发系统整合之Spark IoTDB Connecter
以下 TsFile 结构为例: TsFile 架构中有三个度量:状态、温度和硬件。
108 0
|
4月前
|
iOS开发 MacOS
MAC OS更新系统后IDEA中的SVN报错无法使用
MAC OS更新系统后IDEA中的SVN报错无法使用
|
4月前
|
存储 监控 安全
带你读《Apache Doris 案例集》——07查询平均提速700% ,奇安信基于 Apache Doris 升级日志安全分析系统(1)
带你读《Apache Doris 案例集》——07查询平均提速700% ,奇安信基于 Apache Doris 升级日志安全分析系统(1)
|
4月前
|
SQL 存储 安全
带你读《Apache Doris 案例集》——07查询平均提速700% ,奇安信基于 Apache Doris 升级日志安全分析系统(2)
带你读《Apache Doris 案例集》——07查询平均提速700% ,奇安信基于 Apache Doris 升级日志安全分析系统(2)
110 0
|
10天前
|
消息中间件 存储 Java
深度探索:使用Apache Kafka构建高效Java消息队列处理系统
【4月更文挑战第17天】本文介绍了在Java环境下使用Apache Kafka进行消息队列处理的方法。Kafka是一个分布式流处理平台,采用发布/订阅模型,支持高效的消息生产和消费。文章详细讲解了Kafka的核心概念,包括主题、生产者和消费者,以及消息的存储和消费流程。此外,还展示了Java代码示例,说明如何创建生产者和消费者。最后,讨论了在高并发场景下的优化策略,如分区、消息压缩和批处理。通过理解和应用这些策略,可以构建高性能的消息系统。
|
1月前
|
SQL 分布式计算 HIVE
Apache Hudi入门指南(含代码示例)
Apache Hudi入门指南(含代码示例)
61 0

相关实验场景

更多

推荐镜像

更多