socketserver模块写的一个简单ftp程序
一坨需求...
- 用户加密认证
- 允许同时多用户登录
- 每个用户有自己的家目录 ,且只能访问自己的家目录
- 对用户进行磁盘配额,每个用户的可用空间不同
- 允许用户在ftp server上随意切换目录 (cd)
- 允许用户查看当前目录下文件 (ls)
- 允许上传和下载文件,保证文件一致性(get put)
- 文件传输过程中显示进度条
- 支持文件的断点续传
程序实现
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
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()
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()
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('\n') 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('\n') 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('\n') 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('\n') 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()
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 = '\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
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 + '\n') 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
8、db/user.db
hexm 92df47e9074c048e0afe84ce0a5c407d /tmp 1M
xm 92df47e9074c048e0afe84ce0a5c407d /tmp/sb 5M