socketserver模块写的一个简单ftp程序

一坨需求...

  1. 用户加密认证
  2. 允许同时多用户登录
  3. 每个用户有自己的家目录 ,且只能访问自己的家目录
  4. 对用户进行磁盘配额,每个用户的可用空间不同
  5. 允许用户在ftp server上随意切换目录 (cd)
  6. 允许用户查看当前目录下文件 (ls)
  7. 允许上传和下载文件,保证文件一致性(get put)
  8. 文件传输过程中显示进度条
  9. 支持文件的断点续传

程序实现

1、README

 1 ### 作者信息
 2     姓名: hexm
 3     email: xiaoming.unix@gmail.com
 4 
 5 ### 实现功能
 6     用户加密认证
 7     允许同时多用户登录
 8     每个用户有自己的家目录 ,且只能访问自己的家目录
 9     对用户进行磁盘配额,每个用户的可用空间不同
10     允许用户在ftp server上随意切换目录 (cd)
11     允许用户查看当前目录下文件 (ls)
12     允许上传和下载文件,保证文件一致性(get put)
13     文件传输过程中显示进度条
14     支持文件的断点续传
15 
16 ### 代码目录树
17 ftpserver/
18     ├── bin
19     │   └── ftpserver.py
20     ├── client
21     │   └── ftp.py
22     ├── db
23     │   └── user.db
24     ├── fstab
25     ├── inittab
26     ├── lib
27     │   ├── common.py
28     │   ├── __pycache__
29     │   │   ├── common.cpython-35.pyc
30     │   │   └── user_lib.cpython-35.pyc
31     │   └── user_lib.py
32     ├── src
33     │   ├── ftpserver.py
34     │   └── __pycache__
35     │       └── ftpserver.cpython-35.pyc
36     └── yum.conf
37 
38 ### 功能介绍
39     默认有一个用户,hexm,密码也是hexm,家目录为/tmp,限额1M,
40     pwd 查看用户家目录
41     cd 进入家目录其他目录,并且不能进入其他目录
42     ls 列出家目录下内容
43     put 上传文件 支持断点续传
44     get 下载文件 支持断点续传
45     bye 退出
46 
47 ### 操作步骤
48   * 启动服务端
49     python3 ftpserver/bin/ftpserver.py &
50   * 启动客户端
51     python3 ftpserver/client/ftp.py hexm@127.0.0.1 21
52     >> 输入ftp密码:hexm
53     认证成功
54 
55   * ls 列出当前目录内容
56    列出当前目录内容
57     ftp> ls
58     etc/ fstab inittab pip-53mpg1m2-unpack/ pip-poq_f3ob-unpack/ yum.conf
59    列出某一目录内容
60     ftp> ls etc
61     fstab group inittab
62     ftp> ls /etc
63     fstab group inittab
64    显示多个文件详细信息
65     ftp> ls -l yum.conf fstab
66     -rw-r--r-- 1 root root 970 11月 20 11:46 /tmp/yum.conf
67     -rw-r--r-- 1 root root 396 11月 19 19:44 /tmp/fstab
68 
69   * cd 进入其他目录
70    查看当前目录
71     ftp> pwd
72     /tmp
73    进入家目录中/etc目录
74     ftp> cd /etc
75     ftp> pwd
76     /tmp/etc
77    返回上级目录
78     ftp> cd ..
79     ftp> pwd
80     /tmp
81 
82   * 上传文件 支持断点续传
83    上传bash文件,因为家目录限额为1M,不能上传
84     ftp> put /bin/bash
85     磁盘空间不足
86    上传文件不存在
87     ftp> put /bin/ifstat
88     文件不存在
89    正常上传
90     ftp> put /etc/fstab
91     开始上传[/etc/fstab]
92     ====================================================================================================>100%
93 
94   * 下载文件,支持断点续传
95     发送并校验成功
96     ftp> get yum.conf
97     ====================================================================================================>100%
98   * 退出
99     ftp> bye
View Code

2、目录树

 

 3、bin/ftpserver.py

 1 #!/usr/bin/env python
 2 # coding=utf-8
 3 
 4 import os
 5 import sys 
 6 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 7 from src import ftpserver as src_server
 8 
 9 if __name__ == '__main__':
10         src_server.run()
View Code

4、src/ftpserver.py

  1 #!/bin/python
  2 # coding=utf-8
  3 
  4 import socketserver
  5 import json
  6 import os
  7 import subprocess
  8 import re
  9 
 10 os.sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 11 from lib.user_lib import User
 12 from lib.common import bytes_dumps, str_loads, bytes_encoding, str_encoding, getdirsize
 13 
 14 class FtpServer(socketserver.BaseRequestHandler):
 15 
 16     def handle(self):
 17         self.count = 0
 18         while True:
 19             recv_data = self.request.recv(1024)
 20             if len(recv_data) == 0: break
 21             task_data = str_loads(recv_data)
 22             task_action = task_data.get('action')
 23             # 根据客户端命令执行操作
 24             if hasattr(self, task_action):
 25                 func = getattr(self, task_action)
 26                 func(task_data)
 27 
 28     def auth(self, *args, **kwargs):
 29         """
 30         验证用户合法性
 31         param auth_user: 用户名
 32         param auth_pwd: 密码
 33         param ret: 是否验证成功, 结果为True或False
 34         param self.home_path: 用户家目录
 35         param auth_pass_msg: 发送给客户端是否验证成功的信息
 36         """
 37         auth_user = args[0].get('user')
 38         auth_pwd = args[0].get('pwd')
 39         auth = User(auth_user, auth_pwd)
 40         ret = auth.login()
 41         if ret:  # 认证成功获取用户家目录
 42             auth_pass_msg = {"status": "True"}
 43             self.home_path = auth.home_path()
 44             self.limit_home = auth.limit_home()
 45         else:
 46             auth_pass_msg = {"status": "False"}
 47         self.request.send(bytes_dumps(auth_pass_msg))
 48 
 49     def put(self, *args, **kwargs):
 50         file_size = args[0].get('file_size')
 51         file_name = args[0].get('file_name')
 52         md5sum = args[0].get('md5sum')
 53         abs_file = os.path.join(self.home_path, file_name)
 54         if os.path.isfile(abs_file):  # 上传的文件存在
 55             if self._md5sum(abs_file) == md5sum:  #上传的文件md5和本地的一致,不用重新上传
 56                 status_msg = {"status": "same"}
 57                 self.request.send(bytes_dumps(status_msg))
 58             else:  # 不一致断点续传
 59                 server_file_size = os.stat(abs_file).st_size
 60                 status_msg = {"status": "add", "from_size": server_file_size}
 61                 self.request.send(bytes_dumps(status_msg))
 62                 with open(abs_file, 'ab') as f:
 63                     recv_size = server_file_size
 64                     while recv_size < file_size:
 65                         data = self.request.recv(4096)
 66                         f.write(data)
 67                         recv_size += len(data)
 68         else:  #上传文件不存在,开始写入
 69             # 上传的文件大小加上家目录大小 大于 限制的家目录大小,不允许上传
 70             if getdirsize(self.home_path) + int(file_size) > int(self.limit_home.strip('M')) * 1024 * 1024:  
 71                 status_msg = {"status": "less"}
 72                 self.request.send(bytes_dumps(status_msg))
 73             else:
 74                 status_msg = {"status": "True"}
 75                 self.request.send(bytes_dumps(status_msg))
 76                 with open(abs_file, 'wb') as f:
 77                     recv_size = 0
 78                     while recv_size < file_size:
 79                         data = self.request.recv(4096)
 80                         f.write(data)
 81                         recv_size += len(data)
 82                 request_client_md5 = {"option": "md5"}
 83                 self.request.send(bytes_dumps(request_client_md5))
 84                 md5sum = self._md5sum(abs_file)
 85                 client_md5 = str_loads(self.request.recv(1024))
 86                 if client_md5['md5'] == md5sum:
 87                     check_msg = {"status": "True"}
 88                 else:
 89                     check_msg = {"status": "False"}
 90                 self.request.send(bytes_dumps(check_msg))
 91 
 92     @staticmethod
 93     def _md5sum(filename):
 94         cmd = 'md5sum %s' % filename
 95         ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 96         md5sum = ret.stdout.read().split()[0]
 97         return str(md5sum, encoding='utf-8')
 98 
 99     def get(self, *args, **kwargs):
100         file_name = args[0].get('file_name')
101         abs_filename = os.path.join(self.home_path, file_name)
102         if os.path.isfile(abs_filename):
103 
104             # 文件存在的话,向客户端发送此文件状态信息
105             file_size = os.stat(abs_filename).st_size
106             md5sum = self._md5sum(abs_filename)
107             status_msg = {"status": "True", "file_size": file_size, "md5sum": md5sum}
108             self.request.send(bytes_dumps(status_msg))
109 
110             # 客户端接收状态信息后,接收客户端回复信息
111             client_ack_msg = self.request.recv(1024)
112             client_ack_msg = str_loads(client_ack_msg)
113             if client_ack_msg["status"] == 'True':  # 客户端没有这个文件,完整下载
114                 with open(abs_filename, 'rb') as f:
115                     while True:
116                         filedata = f.read(4096)
117                         if not filedata: break
118                         self.request.send(filedata)
119                 client_request_md5 = str_loads(self.request.recv(1024))
120                 if client_request_md5['option'] == 'md5':
121                     md5sum = self._md5sum(abs_filename)
122                     md5_msg = {"md5": md5sum}
123                     self.request.send(bytes_dumps(md5_msg))
124             elif client_ack_msg['status'] == 'same':  # 客户端和服务端文件相同
125                 pass
126             elif client_ack_msg['status'] == 'add':  # 客户端有文件但不完整
127                 from_size = int(client_ack_msg['from_size'])
128                 with open(abs_filename, 'rb') as f:
129                     f.seek(from_size)
130                     while True:
131                         filedata = f.read(4096)
132                         if not filedata: break
133                         self.request.send(filedata)
134         else:
135             status_msg = {"status": "False"}
136             self.request.send(bytes_dumps(status_msg))
137 
138 
139     def cd(self, *args, **kwargs):
140         if self.count == 0:
141             self.tmp_home_path = self.home_path
142         
143         cd_dir = args[0].get('cd_dir')
144         if cd_dir == '/':
145             self.home_path = self.tmp_home_path
146             status = {"status": "True"}
147         elif cd_dir.startswith('/'):
148             cd_dir = cd_dir[1:]
149             if os.path.isdir(os.path.join(self.home_path, cd_dir)):
150                 self.home_path = os.path.join(self.home_path, cd_dir)
151                 status = {"status": "True"}
152             else:
153                 status = {"status": "False"}
154         elif cd_dir == '':
155             self.home_path = self.tmp_home_path
156             status = {"status": "True"}
157         else:
158             if os.path.isdir(os.path.join(self.home_path, cd_dir)):
159                 self.home_path = os.path.join(self.home_path, cd_dir)
160                 status = {"status": "True"}
161             else:
162                 status = {"status": "False"}
163         status = bytes_dumps(status)
164         self.request.send(status)
165 
166         self.count += 1
167 
168     def pwd(self, *args, **kwargs):
169         cmd = args[0].get('action')
170         res = subprocess.Popen(cmd, shell=True, cwd=self.home_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
171         cmd_res = res.stdout.read()
172         cmd_res_len = len(cmd_res)
173         status_msg = {"status": "ready", "len_ret": cmd_res_len}
174         self.request.send(bytes_dumps(status_msg))
175         ack_msg = self.request.recv(1024)
176         if str_loads(ack_msg).get('status') == 'yes':
177             self.request.send(cmd_res)
178 
179     def ls(self, *args, **kwargs):
180         cmd = args[0].get('action')
181         args = args[0].get('args')
182         if len(args) == 0:
183             fnames = args = ''
184         else:
185             try:
186                 args, fnames = re.search('(.*-w+ ?)(.*)', args).groups()
187             except AttributeError as e:
188                 fnames = args
189                 args = ''
190         cmd_res = ''
191         if len(fnames.strip().split()) > 1:
192             for i in fnames.strip().split():
193                 res = subprocess.Popen(cmd + ' ' + args + ' ' + self.home_path + '/' + i, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
194                 cmd_res += str(res.stdout.read(), encoding='utf-8')
195             cmd_res = bytes(cmd_res, encoding='utf-8')
196         else: 
197             res = subprocess.Popen(cmd + ' ' + args + ' ' + self.home_path + '/' + fnames, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
198             cmd_res = res.stdout.read()
199             if '-l' not in args:
200                 cmd_res = str(cmd_res, encoding='utf-8')
201                 tmp = []
202                 for res in cmd_res.strip().split():
203                     if os.path.isdir(os.path.join(self.home_path, res)):
204                         res = res + '/'
205                     tmp.append(res)
206                 cmd_res = ' '.join(tmp)
207                 cmd_res = bytes(cmd_res, encoding='utf-8')
208 
209         cmd_res_len = len(cmd_res)
210         if not cmd_res:
211             cmd_res = res.stderr.read()
212             cmd_res_len = len(cmd_res)
213         status_msg = {"status": "ready", "len_ret": cmd_res_len}
214         self.request.send(bytes_dumps(status_msg))
215         ack_msg = self.request.recv(1024)
216         if str_loads(ack_msg).get('status') == 'yes':
217             self.request.send(cmd_res)
218 
219 
220     def _bytes_dumps(self, msg_data):
221         return bytes(json.dumps(msg_data), encoding='utf-8')
222 
223     def _str_loads(self, msg_data):
224         return json.loads(str(msg_data, encoding='utf-8'))
225 
226 
227 def main():
228     server = socketserver.ThreadingTCPServer(('0.0.0.0', 21), FtpServer)
229     server.serve_forever()
230 
231 
232 def run():
233     main()
234 
235 
236 if __name__ == '__main__':
237     run()
View Code

5、client/ftp.py

  1 #!/usr/bin/env python
  2 # coding=utf-8
  3 
  4 import subprocess
  5 import socket
  6 import json
  7 import sys
  8 import os
  9 
 10 os.sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 11 from lib.common import bytes_dumps, str_loads, bytes_encoding, str_encoding, view_bar
 12 
 13 class FtpClient(object):
 14 
 15     def __init__(self, fuser, fpwd, fip, fport=21):
 16         self.fuser = fuser
 17         self.fpwd = fpwd
 18         self.fip = fip
 19         self.fport = int(fport)
 20         self.s = socket.socket()
 21 
 22 
 23     def connect_socket(self):
 24         self.s.connect((self.fip, self.fport))
 25 
 26 
 27     def auth(self):
 28         auth_msg = {"action": "auth", "user": self.fuser, "pwd": self.fpwd}
 29         self.s.send(bytes(json.dumps(auth_msg), encoding='utf-8'))
 30         server_ack_msg = self.s.recv(1024)
 31         ack_data = json.loads(server_ack_msg.decode())
 32         if ack_data['status'] == 'True':
 33             return True
 34         else:
 35             return False
 36 
 37 
 38     def send_data(self):
 39         while True:
 40             send_data = input('ftp> ')
 41             if len(send_data) == 0:
 42                 continue
 43             cmd_list = send_data.strip().split()
 44             if cmd_list == 0:
 45                 continue
 46             cmd = cmd_list[0]
 47             if hasattr(self, cmd):
 48                 func = getattr(self, cmd)
 49                 func(cmd_list)
 50             else:
 51                 print('--暂时不支持[%s]命令' % cmd)
 52 
 53 
 54     def put(self, *args):
 55         cmd = args[0][0]
 56         abs_filename = args[0][-1]
 57         if os.path.islink(abs_filename):
 58             print('符号链接不能上传')
 59         elif os.path.isfile(abs_filename):
 60             file_size = os.stat(abs_filename).st_size
 61             file_name = os.path.basename(abs_filename)
 62             md5sum = self._md5sum(abs_filename)
 63             msg_data = {"action": cmd, "file_name": file_name, "file_size":file_size, "md5sum": md5sum}
 64             self.s.send(bytes_dumps(msg_data))
 65             server_ack_msg = self.s.recv(1024)
 66             ack_data = str_loads(server_ack_msg)
 67             if ack_data['status'] == 'True':
 68                 print('开始上传[%s]' % abs_filename)
 69                 with open(abs_filename, 'rb') as f:
 70                     send_size = 0
 71                     while True:
 72                         filedata = f.read(8192)
 73                         send_size += len(filedata)
 74                         if not filedata: break
 75                         self.s.send(filedata)
 76                         view_bar(send_size, file_size) 
 77                     sys.stdout.write('
')
 78                 server_request_md5 = str_loads(self.s.recv(1024))
 79                 if server_request_md5['option'] == 'md5':
 80                     md5sum = self._md5sum(abs_filename)
 81                     md5_msg = {"md5": md5sum}
 82                     self.s.send(bytes_dumps(md5_msg))
 83                 md5_status = str_loads(self.s.recv(1024))
 84                 if md5_status['status'] == 'True':
 85                     print('发送并校验成功')
 86                 else:
 87                     print('上传文件失败')
 88             elif ack_data['status'] == 'add':
 89                 with open(abs_filename, 'rb') as f:
 90                     send_size = f.seek(ack_data['from_size'])
 91                     print('开始上传[%s]' % abs_filename)
 92                     while True:
 93                         filedata = f.read(8192)
 94                         send_size += len(filedata)
 95                         if not filedata: break
 96                         self.s.send(filedata)
 97                         view_bar(send_size, file_size)
 98                     sys.stdout.write('
')
 99                 print('增量上传成功')
100             elif ack_data['status'] == 'same':
101                 print('开始上传[%s]' % abs_filename)
102                 print('====================================================================================================>100%')
103                 print('发送并校验成功')
104             elif ack_data['status'] == 'less':
105                 print('磁盘空间不足')
106         else:
107             print('文件不存在')
108 
109 
110     @staticmethod
111     def _md5sum(filename):
112         cmd = 'md5sum %s' % filename
113         ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
114         md5sum = ret.stdout.read().split()[0]
115         return str(md5sum, encoding='utf-8')
116 
117 
118     def get(self, *args, **kwargs):
119         cmd = args[0][0]
120         abs_filename = args[0][-1]
121         file_name = os.path.basename(abs_filename)
122         msg_data = {"action": cmd, "file_name": file_name}
123 
124         self.s.send(bytes_dumps(msg_data))
125 
126         server_status_msg = self.s.recv(1024)
127         status_msg = str_loads(server_status_msg)
128 
129         if status_msg['status'] == 'True':  # 如果要下载的文件在服务端存在
130             file_size = status_msg['file_size']
131 
132             if os.path.exists(file_name):
133                 if self._md5sum(file_name) == status_msg['md5sum']:  # 本地文件和服务端相同,不用重新下载
134                     ack_msg = {"status": "same"}
135                     self.s.send(bytes_dumps(ack_msg))
136                     print('====================================================================================================>100%')
137                     print('下载并校验成功')
138                 else:  # 文件存在,但校验和不同,说明没有下载完全
139                     local_file_size = os.stat(abs_filename).st_size
140                     ack_msg = {"status": "add", "from_size":local_file_size}
141                     self.s.send(bytes_dumps(ack_msg))
142                     with open(file_name, 'ab') as f:
143                         recv_size = local_file_size
144                         while recv_size < file_size:
145                             data = self.s.recv(4096)
146                             f.write(data)
147                             recv_size += len(data)
148                         view_bar(recv_size, file_size) 
149                     sys.stdout.write('
')
150             else:  # 本地没有这个文件,可以下载
151                 ack_msg = {"status": "True"}
152                 self.s.send(bytes_dumps(ack_msg))
153                 with open(file_name, 'wb') as f:
154                     recv_size = 0
155                     while recv_size < file_size:
156                         data = self.s.recv(4096)
157                         f.write(data)
158                         recv_size += len(data)
159                         view_bar(recv_size, file_size) 
160                     sys.stdout.write('
')
161                 request_server_md5 = {"option": "md5"}
162                 self.s.send(bytes_dumps(request_server_md5))
163                 md5sum = self._md5sum(file_name)
164                 server_md5 = str_loads(self.s.recv(1024))
165                 if server_md5['md5'] == md5sum:
166                     print('下载并校验成功')
167                 else:
168                     print('校验失败')
169         else:
170             print('服务端没有这个文件')
171 
172 
173     def pwd(self, *args, **kwargs):
174         if len(args[0]) != 1:
175             print('错误的参数')
176         else:
177             cmd = args[0][0]
178             msg_data = {"action": cmd}
179             self.s.send(bytes_dumps(msg_data))
180             server_ack_msg = self.s.recv(1024)
181             ack_data = str_loads(server_ack_msg)
182             if ack_data['status'] == 'ready':
183                 ack_msg = {"status": "yes"}
184                 self.s.send(bytes_dumps(ack_msg))
185                 cmd_recv_size = 0
186                 cmd_ret = b''
187                 while cmd_recv_size < ack_data['len_ret']:
188                     cmd_recv = self.s.recv(1024)
189                     cmd_ret += cmd_recv
190                     cmd_recv_size += len(cmd_recv)
191                 print(str(cmd_ret, encoding='utf-8'))
192 
193 
194     def cd(self, *args, **kwargs):
195         if len(args[0]) > 2:
196             print('错误的参数')
197         elif len(args[0]) == 1:
198             cmd = args[0][0]
199             cd_dir = ''
200         else:
201             cmd = args[0][0]
202             cd_dir = args[0][1]
203         msg_data = {"action": cmd, "cd_dir": cd_dir}
204         self.s.send(bytes_dumps(msg_data))
205         status = self.s.recv(1024)
206         status = str_loads(status)
207         if status['status'] == 'False':
208             print('目录不存在')
209 
210 
211     def bye(self, *args, **kwargs):
212         sys.exit(0)
213 
214 
215     def ls(self, *args):
216         cmd = args[0][0]
217         args = ' '.join(args[0][1:])
218         msg_data = {"action": cmd, "args": args}
219         self.s.send(bytes_dumps(msg_data))
220         server_ack_msg = self.s.recv(1024)
221         ack_data = str_loads(server_ack_msg)
222         if ack_data['status'] == 'ready':
223             ack_msg = {"status": "yes"}
224             self.s.send(bytes_dumps(ack_msg))
225             cmd_recv_size = 0
226             cmd_ret = b''
227             while cmd_recv_size < ack_data['len_ret']:
228                 cmd_recv = self.s.recv(1024)
229                 cmd_ret += cmd_recv
230                 cmd_recv_size += len(cmd_recv)
231             print(str(cmd_ret, encoding='utf-8'))
232 
233 
234     def help(self, *args, **kwargs):
235         print('--支持如下命令: ls, cd, pwd, get, put, bye')
236 
237 
238 def main():
239 
240     user, ip = sys.argv[1].strip().split('@')
241     port = sys.argv[2]
242     pwd = input('>> 输入ftp密码:').strip()
243 
244     ftp = FtpClient(user, pwd, ip, port)
245     ftp.connect_socket()
246     ret = ftp.auth()
247     if ret:
248         print('认证成功')
249         ftp.send_data()
250     else:
251         print('认证失败')
252         ftp.bye()
253     ftp.send_data()
254 
255 
256 def run():
257     main()
258 
259 
260 if __name__ == '__main__':
261     run()
View Code

6、lib/common.py

#!/usr/bin/env python
# coding=utf-8

import json
import sys
import os

def bytes_dumps( msg_data):
    return bytes(json.dumps(msg_data), encoding='utf-8')

def str_loads(msg_data):
    return json.loads(str(msg_data, encoding='utf-8'))

def bytes_encoding(msg_data):
    return bytes(msg_data, encoding='utf-8')

def str_encoding(msg_data):
    return str(msg_data, encoding='utf-8')

def view_bar(num, total):
    rate = num / total
    rate_num = int(rate * 100)
    r = '
%s>%d%%' % ("="*rate_num, rate_num, )
    sys.stdout.write(r)
    sys.stdout.flush()

def getdirsize(dir):
    size = 0
    for root, dirs, files in os.walk(dir):
        size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
    return size
View Code

7、lib/user_lib.py

#!/usr/bin/env python
# coding=utf-8

import hashlib
import re
import os

BASEPATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
USERDB = os.path.join(BASEPATH, 'db', 'user.db')


class User(object):
    """
    实现登陆注册功能
    """

    def __init__(self, user, password):
        self.username = user
        self.password = password

    @staticmethod
    def check(match, filedb):
        """
        逐行匹配,查看是否用户名已经注册,不允许重复用户名
        """
        with open(filedb, 'r') as fuser:
            for line in fuser:
                if re.match(match + '\b', line):
                    return False
                else:
                    continue
            return True

    @staticmethod
    def login_check(username, password, filedb):
        """
        登陆验证
        """
        with open(filedb, 'r') as fuser:
            for line in fuser:
                user, pwd, _, _ = line.strip().split()
                if user == username and pwd == password:
                    return True
                else:
                    continue
            return False


    def home_path(self):
        """
        返回用户家目录
        """
        with open(USERDB, 'r') as fpath:
            for line in fpath:
                if line.startswith(self.username):
                    return line.strip().split()[-2]
                else:
                    continue
            return False

    def limit_home(self):
        """
        返回用户家目录限额
        """
        with open(USERDB, 'r') as flimit:
            for line in flimit:
                if line.startswith(self.username):
                    return line.strip().split()[-1]
                else:
                    continue
            return False


    def register(self):
        """
        注册用户
        """
        passobj = hashlib.md5(bytes(self.password, encoding='utf-8'))
        passobj.update(bytes(self.password, encoding='utf-8'))
        secure_password = passobj.hexdigest()

        if self.check(self.username, USERDB):
            with open(USERDB, 'a') as fuser:
                fuser.write(self.username + ' ' + secure_password + '
')
                return True
        else:
            print('用户名已存在')

    def login(self):
        """
        用户登陆
        """
        passobj = hashlib.md5(bytes(self.password, encoding='utf-8'))
        passobj.update(bytes(self.password, encoding='utf-8'))
        secure_password = passobj.hexdigest()
        ret = self.login_check(self.username, secure_password, USERDB)
        if ret:
            return True
        else:
            return False

    def modify(self):
        """
        修改密码
        """
        pass
View Code

8、db/user.db

hexm 92df47e9074c048e0afe84ce0a5c407d /tmp 1M
xm 92df47e9074c048e0afe84ce0a5c407d /tmp/sb 5M
View Code
原文地址:https://www.cnblogs.com/xiaoming279/p/6096208.html