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

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

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

  先来看看原理图:

    问题一的解决方案

wKioL1fXn9uy2Oi7AABpWKFTQDI015.png-wh_50

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

    问题二的解决方案

wKiom1fXoWOCdvBaAACUjpuXDP0943.png-wh_50

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


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


1.安装应用

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

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

#安装依赖包
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的基本配置,

#创建运行用户,注意不要创建/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

    然后其他默认就可以了.

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

#配置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

#修改数据仓库目录/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文件夹是两回事,我们提交和下载的文件都是经过编码解析的,大家心里要又这个概念.

    下面来看操作

#创建仓库目录  
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来加密配置,所以要独立开来讲讲

#进入目录
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权限

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

#编辑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也自带一些模板,就在数据仓库目录里面

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,只要有提交动做,这个脚本就会执行.

#创建或修改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做好免密码登录

#!/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

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

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

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,所以要做脚本运行就要操作这一步

#设置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),所以就有了第二个脚本,强制提交时输入注释:   

#这个是要求在提交时触发的钩子,当然就不是原本那个文件了
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

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.

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

#第一种方法备份命令,详细参数请看下面
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

然后来看第二种

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

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

#第一种方式恢复命令,先创建一个新的版本库
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,只是个配置文件了,记得给读的权限啊

#打开
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,这个就是执行文件,要给执行权限

#!/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;
}
}

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

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

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

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

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

#放到这个目录
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插件的,所以就要多下面这些步骤了

#假设是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

就能出现界面了