美文网首页python小白学习笔记
AJ Kipper:实现邮箱发送验证码功能

AJ Kipper:实现邮箱发送验证码功能

作者: 麻瓜编程 | 来源:发表于2016-04-09 11:08 被阅读368次

    前言
    大家在注册一个产品的时候,一定有这么一个相似的场景:输入用户名,密码,邮箱,点击确认。然后对方服务器向你的邮箱发一个链接或者验证码,你点击邮箱中的链接或者输入验证码,注册成功。而这一篇文章便是利用Python邮箱服务模块来实现这么一个功能,用的是验证码认证。
    个人推荐
    做一个项目(不管多小)之前,首先要了解相应的概念和领域。比如涉及新的网络协议,相关算法等等。如果会涉及一个新的Python模块,除了看别人代码讲解或者官方文档,最好去查看一下帮助或者Python源码,因为你看别人的代码或者官方文档,你只会明白可以这么用,但是你却不知道为什么这么用,或者说你不知道还可以怎么用。我想在这篇文章中分享怎么利用查看源码来了解模块的使用。
    背景知识
    了解邮件服务协议SMTP操作流程
    Python语言提供的库太方便了,基本涉及了各个方面,所以,要实现这个功能,并不复杂,Python在邮箱方面的服务有两个模块,一个是smtplib模块,另一个是email模块。
    email模块
    这个模块主要用来构造邮件内容。毕竟最基本的邮件包括主题,来源,文本内容,更复杂的还有有附件,html格式等等。这里介绍最简单的,也就是纯文本邮件(毕竟只是发个验证码)。
    构造纯文本文件要用到email.mime.text这个模块里面的MIMEText()方法,下面是官方文档的截图
    官方文档:https://docs.python.org/2/library/email.mime.html

    大家英语都很厉害,我就不翻译了哈。
    email.mime.textMIMEText(_text,_subtype[,_charset])中,_text是邮件内容,_subtype是邮件类型,默认是普通类型,值是plain,_charest是编码类型,默认是’us-ascii’,中文文本要加上’utf-8′,’gb2312’等。
    或者可以在Python解释器下导入MIMEText模块,用help(MIMEText)来看看这个类的初始化init函数:

    构造最简单的邮件内容:

    #!/usr/bin/env python
    #-*-coding:utf-8-*-
    #python 2.7.6
     
    __author__ = 'AJ Kipper'
     
    from email.mime.text import MIMEText
     
    msg = MIMEText('Hello,world!','plain','utf-8')
    

    这样,就构造了一个邮件实例msg。内容是Hello,world,类型是plain,编码用utf-8。更复杂的邮件构造请自己去写吧。
    smtplib模块
    这个模块用来发送邮件。首先,在任何语言编程中,编写邮件服务都是需要协议的。我知道的有POP3,SMTP,IMAP,这三个协议都属于应用层,具体的功能和区别可以自己去了解。这里用的是SMTP协议,当然,不用自己去写协议,Python的smtplib模块已经做了很方便的封装,不然你还得自己编写协议,发送还要涉及socket连接。(其实源码中smtplib.py这个文件就是在编写一个SMTP协议)
    查看源码中,我发现smtplib.py中有一个SMTP类,看来里面实现了SMTP协议,并且提供了很多方法。下面是SMTP这个类的初始化init截图:

    
    
    ![](http:https://img.haomeiwen.com/i1692820/a482b2fc2c0d5bd3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    你看,参数有四个,第一个是string类型host,默认是空。第二个是int类型port,默认是0。第三个我估计是测试用的local_hostname,默认是None。第四个是设置socket连接的timeout,默认是socket中的timeout。还有个小细节,你看下面if host:这个代码段,调用了本类中connect( )方法,还传入host和port参数。说明connect函数是进行SMTP协议中的socket连接,连接成功的状态码code是220,那么如果不等于220,当然就返回服务器连接错误信息。所以,在进行调用的时候,你可以直接在实例化SMTP对象的时候传入参数,也可以先不传入参数,而调用SMTP.connect( )方法来传入参数。看下面代码
    ```
    #!/usr/bin/env python
    #-*-coding:utf-8-*-
    #python 2.7.6
     
    __author__ = 'AJ Kipper'
     
    from smtplib import SMTP
    #Just for test
    host = 'smtp.xxx.com'
    port = 0
     
    #第一种连接方法
    server = SMTP(host,port)
     
    #第二种连接方法
    server = SMTP()
    server.connect(host,port)
    ```
    
    
    两种接连方法是等价的。
    然后再来看看connect( )这个函数源码:
    
    ![](http:https://img.haomeiwen.com/i1692820/bc386045b4f50457.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    相信大家还是能大概理解这个函数里面的代码。我读完之后,得出几个信息,1.host字符串还可以直接加: port形成一个URL来作为host参数,因为这个函数里面有正则表达式解析代码段;2.port有一个默认值是default_port,而值就在源码里面,因为用的是self.引用,结果发现默认port = 25。噢,我突然醒悟,确实,SMTP协议默认端口号就是25。接下来调用两个函数,一个是_get_socket( ),一个是getreply( ),注意,如果你对Python面对对象编程很熟悉的话,你肯定知道函数名前面加一个_是代表什么意思。比如这里,为什么不是get_socket( ),而是_get_socket( )?想知道原因,可以用关键词【Python 私有变量】去搜索引擎查一下,或者看一下Python的编码风格标准PEP 0008:
    [https://www.python.org/dev/peps/pep-0008/](https://www.python.org/dev/peps/pep-0008/)
    ,下面是文档截图:
    
    ![](http:https://img.haomeiwen.com/i1692820/e0c82cb31cc27829.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    在Python语言中,并没有private或者public之类的关键字来修饰成员变量,而是通过变量名加下划线来区分。变量前面加一个_说明这个变量不能用from xxx import 导入;变量前面加两个_说明这个变量只能在本类中调用。具体可以自己去尝试,现在还是回到源码本身。
    实例化之后,实例化对象server就具有了这个类的全部方法。
    **SMTP流程**
    如果只是想完成这么一个项目,那其实不懂原理也是可行的,毕竟代码就那几行,也不难。但如果因为一个项目,从而熟悉了SMTP协议,那其实这才是最大的收获。所以,去看看SMTP协议的规定,也是非常必要的。维基百科里面说SMTP最新修订在FRC1869里,以下是链接:[https://tools.ietf.org/html/rfc1869](https://tools.ietf.org/html/rfc1869)。
    要了解SMTP的使用,那么可以通过SMTP协议的命令,在终端直接连接邮件服务器来看看SMTP的操作流程,我选的服务器是微软Outlook的,如下:
    [](http://cn.micronoob.com/wp-content/uploads/sites/4/2016/04/Screen-Shot-2016-04-07-at-19.41.20.png)
    
    ![](http:https://img.haomeiwen.com/i1692820/644172d47b88423b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    第一条命令[telnet smtp-mail.outlook.com 25]是用telnet来尝试连接outlook的邮件服务器,服务器地址在outlook的官网,一查就有,端口是25。服务器回复代码是220,连接成功。
    第二条命令HELO是表名身份,后面的字符串可以随便填写。这是为了告诉服务器一些客户端机器的信息。服务器返回250,成功Say Hello。
    第三条命令auth login,我要进行用户登录的时候,提示Must issue a STARTTLS comand first,说明,服务器要求在登录之前必须要使用TLS加密。
    好,然后我再次按照提示连接的时候,服务器竟然自己关闭了。这里我也不懂为啥,懂的朋友可以告诉我原因,如下:
    [](http://cn.micronoob.com/wp-content/uploads/sites/4/2016/04/Screen-Shot-2016-04-07-at-19.46.44.png)
    
    ![](http:https://img.haomeiwen.com/i1692820/e356db1a97f7471e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    然后,我换一个邮件服务器试试,用的是163邮箱,发现不用STARTTLS命令,也连接成功了。不过我没有163邮箱,所以没有进行登录,如下:
    [](http://cn.micronoob.com/wp-content/uploads/sites/4/2016/04/Screen-Shot-2016-04-07-at-19.50.27.png)
    
    ![](http:https://img.haomeiwen.com/i1692820/84b686e753eac83c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    经过这么一番周折,大概了解了SMTP操作步骤,一般是这样的顺序:连接服务器  → 身份验证 → 选择加密传输方式 →登录 → 操作 → 断开连接。
    所以,可以去看看源码,是否有这些功能的函数存在。研究一段时间之后,你会发现源码SMTP类中存在login( ),starttls( ),helo( ),sendmail( ),close( )等函数,然后按照步骤调用就行,我是都去看了源码,但不在这里讲了,通过刚才connect( )函数的例子,相信你已经明白怎么通过查看源码来学习使用刚接触的函数。
    下面是一个最基本的流程:
    ```
    #!/usr/bin/env python
    #-*-coding: utf-8-*-
    #python 2.7.6
     
    'Email server module'
     
    __author__ = 'AJ Kipper'
     
    import smtplib
    smtp_server = 'smtp-mail.outlook.com:25'
     
    server = smtplib.SMTP(smtp_server)
    server.helo()
    server.starttls()
    server.login(mail_account,mail_password)
    server.sendmail('邮件内容')
    server.close()
    ```
    
    **总结**
    到这里,这个项目最核心的东西都说完了。就两方面,一个是email.mime.text里MIMEText的邮件构造,二是smtplib模块SMTP类的使用,其他的不过就是一些基本的Python知识,比如产生伪随机数发送给目标邮箱进行验证,函数编写和调用等等,毕竟还是要自己去尝试和思考,接着才是去看我的源码,源码在我的github:[https://github.com/AJKipper/PythonScript/tree/master/SMTP_Server](https://github.com/AJKipper/PythonScript/tree/master/SMTP_Server),下面也贴出来。
    **源码**
    源码有两个文件,一个是emailserver.py模块,用来编写邮件和发送邮件。另一个是register.py模块,调用emailserver模块进行随机数验证。
    **emailserver.py**
    ```
    #!/usr/bin/env python
    #-*-coding: utf-8-*-
    #python 2.7.6
     
     
    'Email server module'
     
    __author__ = 'AJ Kipper'
     
    import smtplib
    from email.mime.text import MIMEText
     
     
    class email_server:
     def __init__(self,user_email,code):
     #设置邮件服务地址及默认端口号,这里选择的是outlook邮箱
     self.smtp_server = 'smtp-mail.outlook.com:25'
                    #设置发送来源的邮箱地址
     self.mail_account = 'Your email account'
     self.mail_passwd = 'Your email password'
                    self.user_email = user_email
                    #发件箱的后缀m
     self.mail_postfix = 'outlook.com'
                    #subject代表邮件主题信息
     self.subject = 'Email authentication from AJ Kipper'
     self.form_msg = "This is a mailbox validation from" + "<" + self.mail_account + "@" + self.mail_postfix + ">"
     self.content = 'Hi,thank you for registering the chat room created by AJ Kipper!\nYour email authentication code is ' + code
            def send_email(self):
                    global send_status
                    #普通文本邮件
             msg = MIMEText(self.content,_subtype='plain',_charset='utf-8')  
             msg['Subject'] = self.subject
             msg['From'] = self.form_msg  
             msg['To'] = ";".join(self.user_email)
             try:
             server = smtplib.SMTP(self.smtp_server)  
             #server.connect(self.smtp_server)
                            #返回服务器特性
             server.ehlo()
                            #进行TLS安全传输
             server.starttls()
             server.login(self.mail_account,self.mail_passwd)  
             server.sendmail(self.form_msg, self.user_email, msg.as_string())  
             server.close() 
                            return True
             except Exception, error:  
             print str(error)  
             return False
    if __name__ == '__main__':
     email_server()
    ```
    **register.py**
    ```
    #!/usr/bin/env python
    #-*-coding: utf-8-*-
    #python 2.7.6
     
     
    'register server module'
     
    __author__ = 'AJ Kipper'
    from random import randint
    import emailserver
     
    class register:
     def __init__(self,reg_email):
     #创建一个四个号码的伪随机数作为验证码
     code = randint(1000,2000)
     self.reg_email = reg_email
     self.code = str(code)
     self.send_status = False
     def verify(self):
     email = emailserver.email_server(self.reg_email,self.code)
     if email.send_email():
     print "Already sent!"
     else:
     print "Fail in send!"
     self.get_code()
     def get_code(self):
     code = raw_input('code:')
     while code != self.code:
     print 'Verification code is incorrect!'
     code = raw_input('code:')
     print "Verify success"
     
    if __name__ == '__main__':
     #要发送邮箱地址
     test = register(['要发送的邮箱地址'])
     test.verify()
    ```

    相关文章

      网友评论

        本文标题:AJ Kipper:实现邮箱发送验证码功能

        本文链接:https://www.haomeiwen.com/subject/vhxblttx.html