• 登录验证程序-[sys][datetime][json][hashlib]模块的运用


    程序目录结构

    ├── account
    │   └── 1234.json
    └── bin
          └── user_login.py
    

    1234.json

    {
      "expire_date": "2020-01-01",
      "id": 1234,
      "status": 0,
      "pay_day": 22,
      "password": "950b80f9ec4a84bdb828400a404e55d5",
      "salt": "eq8nXoV9Ndh6"
    }
    

    user_login.py

    # -*- coding: utf-8 -*-
    # @Author  : 'hhjie'
    # @Time    : 2018-05-14 13:18
    # @File    : 综合练习-01---用户登录-json.py
    
    """
    {"expire_date": "2021-01-01", "id": 1234, "status": 0, "pay_day": 22, "password": "abc"}
    
    ①:
    1、用户名为json文件名,密码为 password。
    2、判断是否过期,与expire_date进行对比。
    3、登陆成功后,打印“登陆成功”,三次登陆失败,status值改为1,并且锁定账号。
    
    分析:
    1、用户名为json文件名,意思是每个用户对应一个json文件。如果用户输入用户名,则在用户文件夹中寻找是否包含该用户的用户文件。要有读取json文件的函数。
    2、判断是否过期,意思为通过时间模块获取到年月日。和文件中的"expire_date"日期进行比对。由于是字符串类型。需要转换为时间戳。
        用当前时间减去"expire_date"的时间。如果结果为负数或者0.则未过期,反之。如果大于0,则过期。
    3、三次登录失败后,要修改状态。要写入文件。需要单独写一个写入文件的函数。锁定账号,就是在判断中,如果被锁定了,直接退出。
    
    ②:
    三次验证的密码进行hashlib加密处理。即:json文件保存为md5的值,然后用md5的值进行验证。
    独立的函数用hashlib处理密码加密
    
    分析:
    由于hashlib可通过反推表反推出字符串。所有我这处理的方式是将密码进行加盐操作,得到唯一的md5值。
    这个'盐'利用随机字符生成,并保存在用户列表中。用变量 user_salt 表示。当然这个salt和用户之间在真实数据库中需要另外一张表对应。
    
    """
    
    import os
    import json
    import string
    import random
    import datetime
    import hashlib
    
    
    def load_user_info(user_file):
        user_info = json.load(open(user_file, mode='r', encoding='utf-8'))
        return user_info
    
    
    def dump_user_info(user_info, user_file):
        temp_file = user_file + ".bak"
        json.dump(user_info, open(temp_file, mode='w', encoding='utf-8'))
    
        os.remove(user_file)
        os.rename(temp_file, user_file)
    
    
    def get_files_of_dir(dir_path):
        all_files_dirs = os.listdir(dir_path)
        return all_files_dirs
    
    
    def is_expired(expire_date):
        today = datetime.datetime.today()
        expire_day = datetime.datetime.strptime(expire_date, "%Y-%m-%d")
        # 注意:传入的参数时间字符串为:【"2021-01-01"】但是经过转换为元组时间时,变为【"2021-01-01 00:00:00"】
        delta = today - expire_day  # 结果为3 days, 0:00:00 这种形式的时间格式。其中有很多方法。days拿到天数。
        if delta.days > 0:  # 如果比对日期为正数的话,则过期了。返回True,意为过期为真。
            print("您的账号已过期,请联系管理员")
            return True
        else:
            print("账号离过期还剩【{0}】天".format(abs(delta.days)))
            return False
    
    
    def encrypt_password(password, salt=None):
        # 两个功能,一是新用户创建时,未给定salt,则随机生成salt并加密密码,返回salt和hash值。二是非新用户登录时,从文件信息中获取到salt和密码加密
    
        if not salt:  # 如果salt没有值,就将随机字符串赋值给salt。
            salt = ''.join(
                random.sample(string.digits + string.ascii_letters, 12))  # 生成16位的随机字符串作为加密的salt。
        password_hash = hashlib.md5()
        password_hash.update((password + salt).encode('utf-8'))  # 将密码和salt进行合并字符串进行加密
        return salt, password_hash.hexdigest()
    
    
    def start():
        try_count = 0
        exit_flag = False
        while not exit_flag:
            username = input("请输入用户名:")
            username_file_name = username + ".json"
            username_file_path = os.path.join(account_dir, username_file_name)
            if username_file_name in get_files_of_dir(account_dir):
                username_info = load_user_info(username_file_path)  # 从用户json文件中获取用户信息
                expire_date = username_info["expire_date"]
                is_expire = is_expired(expire_date)
                if is_expire:
                    exit_flag = True
                while not exit_flag:
                    if username_info["status"] == 1:
                        print("您的账号已被锁定,请联系系统管理员。")
                        exit_flag = True
                        break
                    if try_count == 3:  # 先判断尝试次数是否等于3次。
                        username_info["status"] = 1
                        dump_user_info(username_info, username_file_path)  # 这里的文件一定要是绝对路径
                        print("尝试登录系统次数超限,账号已被锁定。")
                        exit_flag = True
                        break  # 这里要有break,因为虽然这里为True,整个系统退出了,但是还会执行下面的代码,因此要用break。下面代码不继续执行了。
                    user_password = input("请输入密码: ")
                    user_encrypt_password = encrypt_password(user_password, username_info["salt"])[1]  # 获取加密密码的hash值。
                    if user_encrypt_password == username_info["password"]:
                        print("欢迎您【{0}】,登录系统成功".format(username))
                        exit_flag = True
                    else:
                        print("密码错误,请重试...")
                        try_count += 1
                        left_try_count = 3 - try_count
                        if left_try_count == 0:  # 如果剩余尝试次数为0的话,就跳过下面代码。
                            continue
                        print("还有{0}次尝试登录系统的机会!".format(left_try_count))
            else:
                print("用户名不存在,请重试")
    
    
    if __name__ == '__main__':
        BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # 获取当前绝对路径的目录名
        account_dir = os.path.join(BASE_DIR, "account")  # 用户账户目录
        start()
    
  • 相关阅读:
    烧写NAND Flash时出现错误:*** Warning bad CRC or NAND, using default environment
    在ubuntu下如何验证文件的MD5码
    条件编译#ifdef MACRO_A和#if defined(MACRO_A)的区别
    用nmap获取ip和mac地址
    rcS中启动udevd
    ubuntu下minicom不能接受键盘输入
    ios audioqueue 流播放接口
    ffmpeg 0.8.11 VC编译的SDK已经发布
    lua 字符串数学表达式运算
    ffmpeg 0.8.11 VC编译的SDK已经发布
  • 原文地址:https://www.cnblogs.com/a72hongjie/p/9043966.html
Copyright © 2020-2023  润新知