通过Python制作一个MFA验证器

在文章开始之前我必须要吐槽一下,Windows的无线局域网优化的是多么优秀!!!啊啊啊啊啊!!!!!
请添加图片描述
当事人:当时我刚写完稿关掉VSCode,准备投稿的时候。对,就是这么巧,小说都不敢写的这么巧。WiFi嘎了。我感到比较有意思,就顺手发到了网上,不知道火没火[doge(脚动)]


我们经常会遇到一些需要验证动态密码的页面,这个时候在手机上有很多此类的MFA应用,比如Microsoft Authenticator,Duo Mobile,谷歌验证器。

那么其实我们也可以自己做一个MFA验证器。本着只要网上有,绝不自己做的原则,我们将使用开源模块拼凑而成。

这里只讲解它的原理,图形方案交给你们设计。


环境准备

首先我们需要安装pyotp(获取动态口令)和heframework(简化开发过程),这两个模块均可以通过pip安装。上述模块需要在Python 3.6及以上版本才可以运行。

规划方案

它首先可以获取动态口令,这是它最直接的功能。获取动态密码需要对应的Secure,所以它也需要管理Secure的功能。管理面板需要包含添加,删除这些基本操作。

所以我们计划使用json文本交换格式作为Secure的存储介质,使用list分别存储Secure及其对应的名称。


大纲代码

我们首先需要根据主程序图写出一个大纲,以准备后续填充代码。这里需要参考一下heframeworkchoose模块用法

import heframework
import sys


# 获取令牌
def get():
    pass


# 管理令牌
def console():
    pass


# main
def main():
    while True:
        main = heframework.choose(
            mode="list", name=["获取令牌", "管理令牌", "退出"], return_text=[1, 2, 3], info=">>>")
        if main == 1:
            get()
        elif main == 2:
            console()
        elif main == 3:
            sys.exit()
        else:
            print("Err")


if __name__ == "__main__":
    main()

管理令牌

大纲

根据上面的内容,我们可以写出管理界面的选择器。

# 添加
def add():
    pass


# 删除
def delete():
    pass


# 大纲
def console():
    while True:
        main = heframework.choose(
            mode="list", name=["添加令牌", "删除令牌", "退出"], return_text=[1, 2, 3], info=">>>")
        if main == 1:
            add()
        elif main == 2:
            delete()
        elif main == 3:
            break
        else:
            print("Err")
添加令牌

在这里就涉及到了json的操作和文件的读写操作。我们还是继续使用heframework作代码的简化。

这里代码执行的是顺序操作。首先需要用户录入令牌的名称和Secure。需要检验是否存在token.json文件,如果不存在则新建一个。如果该文件存在则读取该文件,并确保不存在重复的值。

如果以上判断返回的值为true,先将namesecure对应的list读取到内存,将新增的值通过append()写入,再将内存中的值重新写入json文件中。

import json
import os
import time


def add():
    name = input("令牌名称:")
    secure = input("令牌Secure:")
    if not os.path.exists("token.json"):
        file = open("token.json", "w")
        dicts = {"name": [name], "return_text": [secure]}
        file.write(json.dumps(dicts))
        time.sleep(1)
        file.close()
        print("Successfully!")
    else:
        read_only_file = open("token.json")
        dicts = json.load(read_only_file)
        read_only_file.close()
        if name in dicts["name"]:
            print("Existed!")
        else:
            if secure in dicts["return_text"]:
                print("Existed!")
            else:
                file_after = open("token.json", "w")
                names = dicts["name"]
                secures = dicts["return_text"]
                names.append(name)
                secures.append(secure)
                dict_after = {"name": names, "return_text": secures}
                file_after.write(json.dumps(dict_after))
                time.sleep(1)
                file_after.close()
                print("Successfully!")
删除令牌

这里依旧使用顺序结构。将token.json读取后,遍历列表。当用户选择后,通过del对应的选项实现删除操作。

import json
import os
import time

def delete():
    if not os.path.exists("token.json"):
        print("No file!")
    else:
        read_only_file = open("token.json")
        dicts = json.load(read_only_file)
        read_only_file.close()
        if not dicts["name"]:
            print("No Token!")
        else:
            num = 0
            for i in dicts["name"]:
                num += 1
                print(num, ": ", i)
            choose = int(input("请输入你要删除的项目:"))
            if choose >= 1 and choose <= num:
                names = dicts["name"]
                secures = dicts["return_text"]
                del names[int(choose-1)]
                del secures[int(choose-1)]
                print("Successfully!")
                file_after = open("token.json", "w")
                dict_after = {"name": names, "return_text": secures}
                file_after.write(json.dumps(dict_after))
                time.sleep(1)
                file_after.close()
            else:
                print("Err")

获取令牌

当然,最重要的东西要留在最后,这部分内容是这个程序最重要的部分。

我们使用pyotp作token的运算,如果想了解这部分内容可以去搜索一下TOTP

pyotp输出的6位数临时密码通常是几秒变化一次,通过我们的了解,其他同类软件的变化周期通常是30s

根据这些我们就可以写出一个动态密码显示器。

import heframework
import sys
import json
import os
import time
import pyotp


def get():
    if not os.path.exists("token.json"):
        print("No File")
    else:
        read_only_file = open("token.json")
        dicts = json.load(read_only_file)
        read_only_file.close()
        if not dicts["name"]:
            print("No Token!")
        else:
            secure = heframework.choose(
                mode="json", json_file="token.json", info="选择令牌:")
            storage = pyotp.TOTP(str(secure))
            win = heframework.refresh_show(title="令牌", geometry="200x100")
            while True:
                t = 30
                token_now = storage.now()
                for i in range(30):
                    code = win.refresh(str(token_now+"\n剩余时间"+str(t)+"秒"))
                    t -= 1
                    time.sleep(1)
                    if code == 21099:
                        sys.exit()
                    elif code == 20000:
                        pass
                    else:
                        sys.exit()

代码汇总和测试

总结代码如下

import heframework
import sys
import json
import os
import time
import pyotp


# 获取令牌
def get():
    if not os.path.exists("token.json"):
        print("No File")
    else:
        read_only_file = open("token.json")
        dicts = json.load(read_only_file)
        read_only_file.close()
        if not dicts["name"]:
            print("No Token!")
        else:
            secure = heframework.choose(
                mode="json", json_file="token.json", info="选择令牌:")
            storage = pyotp.TOTP(str(secure))
            win = heframework.refresh_show(title="令牌", geometry="200x100")
            while True:
                t = 30
                token_now = storage.now()
                for i in range(30):
                    code = win.refresh(str(token_now+"\n剩余时间"+str(t)+"秒"))
                    t -= 1
                    time.sleep(1)
                    if code == 21099:
                        sys.exit()
                    elif code == 20000:
                        pass
                    else:
                        sys.exit()


# 管理令牌

# 添加
def add():
    name = input("令牌名称:")
    secure = input("令牌Secure:")
    if not os.path.exists("token.json"):
        file = open("token.json", "w")
        dicts = {"name": [name], "return_text": [secure]}
        file.write(json.dumps(dicts))
        time.sleep(1)
        file.close()
        print("Successfully!")
    else:
        read_only_file = open("token.json")
        dicts = json.load(read_only_file)
        read_only_file.close()
        if name in dicts["name"]:
            print("Existed!")
        else:
            if secure in dicts["return_text"]:
                print("Existed!")
            else:
                file_after = open("token.json", "w")
                names = dicts["name"]
                secures = dicts["return_text"]
                names.append(name)
                secures.append(secure)
                dict_after = {"name": names, "return_text": secures}
                file_after.write(json.dumps(dict_after))
                time.sleep(1)
                file_after.close()
                print("Successfully!")


# 删除
def delete():
    if not os.path.exists("token.json"):
        print("No file!")
    else:
        read_only_file = open("token.json")
        dicts = json.load(read_only_file)
        read_only_file.close()
        if not dicts["name"]:
            print("No Token!")
        else:
            num = 0
            for i in dicts["name"]:
                num += 1
                print(num, ": ", i)
            choose = int(input("请输入你要删除的项目:"))
            if choose >= 1 and choose <= num:
                names = dicts["name"]
                secures = dicts["return_text"]
                del names[int(choose-1)]
                del secures[int(choose-1)]
                print("Successfully!")
                file_after = open("token.json", "w")
                dict_after = {"name": names, "return_text": secures}
                file_after.write(json.dumps(dict_after))
                time.sleep(1)
                file_after.close()
            else:
                print("Err")


# 大纲
def console():
    while True:
        main = heframework.choose(
            mode="list", name=["添加令牌", "删除令牌", "退出"], return_text=[1, 2, 3], info=">>>")
        if main == 1:
            add()
        elif main == 2:
            delete()
        elif main == 3:
            break
        else:
            print("Err")


# main
def main():
    while True:
        main = heframework.choose(
            mode="list", name=["获取令牌", "管理令牌", "退出"], return_text=[1, 2, 3], info=">>>")
        if main == 1:
            get()
        elif main == 2:
            console()
        elif main == 3:
            sys.exit()
        else:
            print("Err")


if __name__ == "__main__":
    main()

运行界面

后记

欢迎参观我的博客 https://www.hestudio.net

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在JavaMail中实现MFA验证需要您先在您的邮件服务提供商的账户设置中启用MFA功能。然后,您需要在JavaMail中设置您的账户凭据以及MFA验证所需的其他参数。 以下是一个使用Google邮件(Gmail)服务的JavaMail MFA验证示例: ```java import java.util.Properties; import javax.mail.*; import javax.mail.internet.*; public class JavaMailMFAExample { public static void main(String[] args) throws Exception { String username = "your_email_address@gmail.com"; String password = "your_password"; String mfaToken = "your_mfa_token"; // MFA token generated by your authenticator app Properties props = new Properties(); props.put("mail.smtp.auth.mechanisms", "XOAUTH2"); props.put("mail.smtp.auth.xoauth2.disable", "false"); props.put("mail.smtp.auth.login.disable", "true"); props.put("mail.smtp.auth.plain.disable", "true"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.host", "smtp.gmail.com"); props.put("mail.smtp.port", "587"); Session session = Session.getInstance(props); session.setDebug(true); Authenticator auth = new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password + mfaToken); } }; MimeMessage msg = new MimeMessage(session); msg.setFrom(new InternetAddress("your_email_address@gmail.com")); msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("recipient_email_address")); msg.setSubject("Test email"); msg.setText("Hello, this is a test email."); Transport transport = session.getTransport("smtp"); transport.connect("smtp.gmail.com", username, password + mfaToken); transport.sendMessage(msg, msg.getAllRecipients()); transport.close(); System.out.println("Message sent successfully."); } } ``` 在此示例中,您需要用您的账户凭据替换示例代码中的`username`和`password`变量。在发送电子邮件之前,您需要使用您的MFA令牌替换示例代码中的`mfaToken`变量。此外,您需要在JavaMail的配置中设置`mail.smtp.auth.mechanisms`属性为`XOAUTH2`,并将`mail.smtp.auth.xoauth2.disable`、`mail.smtp.auth.login.disable`和`mail.smtp.auth.plain.disable`属性设置为`false`。 请注意,不同的邮件服务提供商可能需要不同的配置参数来启用MFA验证。因此,建议您查阅您的邮件服务提供商的文档以了解如何在JavaMail中实现MFA验证

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醉、倾城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值