Python_Day35_Socket模块3和hmac模块详解编程语言

验证客户端链接的合法性

如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,
那么可以利用hmac+加盐的方式来实现。

1:简单的服务端如下 
#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
import os 
import socket 
import hmac 
 
secret_key = '老衲洗头用飘柔'.encode('utf-8') 
server = socket.socket() 
server.bind(('127.0.0.1', 9527)) 
server.listen() 
while True: 
    try: 
        conn, addr = server.accept() 
        random_bytes = os.urandom(32) 
        conn.send(random_bytes) 
        hmac_obj = hmac.new(key=secret_key, msg=random_bytes) 
        ret = hmac_obj.hexdigest() 
        print(hmac_obj.hexdigest()) 
        msg = conn.recv(1024).decode('utf-8') 
        if msg == ret: 
            print('是合法的客户端') 
        else: 
            print('不是合法的客户端') 
            conn.close() 
    finally: 
        server.close() 
        break 
 
客户端如下: 
#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
import socket 
import hmac 
 
secret_key = '老衲洗头用飘柔'.encode('utf-8') 
client = socket.socket() 
client.connect(('127.0.0.1', 9527)) 
 
urandom = client.recv(1024) 
hmac_obj = hmac.new(key=secret_key, msg=urandom) 
client.send(hmac_obj.hexdigest().encode('utf-8')) 
print('--------') 
client.close() 
 
效果如下: 
33e40f5f66b2e9b2867a7862d02fba9d 
是合法的客户端。 
 
例2:TCP时间戳服务器验证 
#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
import os 
import hmac 
from socket import * 
from time import ctime 
 
''' 
socket tcp时间戳服务端,利用hmac模块加盐验证客户端连接的合法性 
''' 
 
# 加盐 
secret_key = '芝麻开门'.encode('utf-8') 
 
def conn_auth(conn): 
    ''' 
    认证客户端链接 
    :param conn: 客户端链接 
    :return: True or False 
    ''' 
    print('开始验证新链接的合法性') 
    # os模块生成随机32位字符串,用于发送给客户端验证 
    ustr = os.urandom(32) 
    conn.sendall(ustr) 
    # 生成密钥 hmac加盐+32位随机字符串 
    cipher = hmac.new(secret_key, ustr).digest() 
    # 接收客户端发送过来的密钥,长度和这边生成的应当一致 
    result = conn.recv(len(cipher)) 
    # compare 两相比较,一致返回true,否则为false 
    return hmac.compare_digest(result, cipher) 
 
def data_handler(conn,bufsize=1024): 
    # 如果验证不通过 
    if not conn_auth(conn): 
        print('链接非法,关闭') 
        conn.close() 
        return 
    print('链接已通过验证,开始通信') 
    while True: 
        data = conn.recv(bufsize) 
        if not data:break 
        data = '[%s] %s' % (ctime(), data.decode('utf-8')) 
        conn.sendall(data.encode('utf-8')) 
    conn.close() 
 
def server_handler(host,port,bufsize=1024,num=5): 
    ''' 
    socket tcp服务端设置 
    :param host: 主机名或ip地址 
    :param port: 端口号 
    :param bufsize: 缓冲区大小,默认1024 
    :param num: 侦听最大客户端,默认5位 
    :return:  
    ''' 
    tcpss = socket(AF_INET, SOCK_STREAM) 
    tcpss.bind((host,port)) 
    tcpss.listen(num) 
    while True: 
        conn, addr = tcpss.accept() 
        print('新连接[%s:%s]' % (addr[0], addr[1])) 
        data_handler(conn,bufsize) 
 
if __name__ == '__main__': 
    host = 'localhost' 
    port = 9527 
    server_handler(host, port) 
 
TCP时间戳客户端: 
#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
''' 
socket tcp时间戳客户端,利用hmac模块加盐验证客户端连接的合法性 
''' 
 
import os 
import hmac 
from socket import * 
 
secret_key = '芝麻开门'.encode('utf-8') 
 
def conn_auth(conn): 
    ''' 
    验证客户端到服务器的链接 
    :param conn: 链接 
    :return: True or False 
    ''' 
    # 客户端接收32位随机字节 
    ustr = conn.recv(32) 
    # hmac加盐加密文得出最终密钥并发送回服务端 
    cipher = hmac.new(secret_key, ustr).digest() 
    conn.sendall(cipher) 
 
def client_handler(host,port,bufsize=1024): 
    tcpsc = socket(AF_INET, SOCK_STREAM) 
    tcpsc.connect((host, port)) 
 
    conn_auth(tcpsc) 
 
    while True: 
        data = input('>>>').strip() 
        if not data:continue 
        if data == 'quit':break 
 
        tcpsc.sendall(data.encode('utf-8')) 
        result = tcpsc.recv(bufsize) 
        print(result.decode('utf-8')) 
 
    tcpsc.close() 
 
if __name__ == '__main__': 
    host = 'localhost' 
    port = 9527 
    bufsize = 1024 
    client_handler(host, port, bufsize) 
 
效果: 
服务端: 
D:/PortableSoft/Python35/python.exe E:/Python/重要的代码/socket_hmac验证合法连接/tcpss.py 
新连接[127.0.0.1:57600] 
开始验证新链接的合法性 
链接已通过验证,开始通信 
 
客户端: 
D:/PortableSoft/Python35/python.exe E:/Python/重要的代码/socket_hmac验证合法连接/tcpsc.py 
>>>time 
[Thu May 10 22:00:19 2018] time 
>>>也许猪的身体不优美,长鼻短尾,但是别人不可天空里高飞 
[Thu May 10 22:01:10 2018] 也许猪的身体不优美,长鼻短尾,但是别人不可天空里高飞 
>>>

socketserver

SocketServer是标准库中的一个高级模块(python3.x中重命名为socketserver),
它的目标是简化很多样板代码,它们是创建网络客户端和服务器所必需的代码。
这个模块中有为你创建的各种各样的类,如下表所示:

除了为你隐藏了实现细节之外,另一个不同之处是,我们现在使用类来编写应用程序,
以面向对象的方式处理事务有助于组织数据,以及逻辑性地将功能放在正确的地方。
你还会注意到,应用程序现在是事件驱动的,这意味着只有在系统中的事件发生时,它们才会工作。

事件包括消息的发送和接收。

事实上,你会看到类定义只包括一个用来接收客户端消息的事件处理程序。
所有其它的功能都来自使用的SocketServer类。
在原始服务器循环中,我们阻塞等待请求,当接收到请求时就对其提供服务,然后继续等待。
在此处的服务器循环中,并非在服务器中创建代码,而是定义一个处理程序,
这样当服务器接收到一个传入的请求时,服务器就可以调用你的函数。

创建SocketServer TCP服务器

#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
''' 
通过使用socketserver类、TCPServer和StreamRequesthandler,该脚本创建了一个时间戳TCP服务器。 
''' 
 
from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH) 
from time import ctime 
 
HOST = '127.0.0.1' 
PORT = 9527 
ADDR = (HOST, PORT) 
 
class MyRequestHandler(SRH): 
 
# 重写handle方法,该方法在基类Request中默认情况下没有任何行为(pass) 
# 但当接收到一个客户端的消息时,它就会调用handle()方法,因此得重写进行处理。 
    def handle(self): 
        print('...connected from:', self.client_address) 
        # StreamRequsetHandler将输入和输出套接字看作类似文件的对象 
        # 因此可以使用readline()获取客户端消息,当然此时客户端要约定消息附带/n换行符 
        data = '[%s] %s' % (ctime(), self.rfile.readline().decode('utf-8')) 
        # 同理,视作文件对象,使用write()将字符串发送回客户端 
        self.wfile.write(data.encode('utf-8')) 
 
tcpServ = TCP(ADDR, MyRequestHandler) 
print('waiting for connection...') 
# 注:是serve,而不是server;forever为无限循环地等待并服务于客户端请求。 
tcpServ.serve_forever() 
 
创建SocketServer TCP客户端 
#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
from socket  import * 
 
HOST = 'localhost' 
PORT = 9527 
BUFSIZ = 1024 
ADDR = (HOST, PORT) 
 
while True: 
    ''' 
    和之前socker普通的tcp客户端从输入才开始循环不同, 
    sockerserver请求处理程序的默认行为是接受连接、获取请求,然后关闭连接。 
    由于这个原因,我们不能在应用程序整个执行过程中都保持连接,因此每次向服务器发送消息时, 
    都需要创建一个新的套接字。 
    ''' 
    tcpCliSock = socket(AF_INET, SOCK_STREAM) 
    tcpCliSock.connect(ADDR) 
    data = input('>>>') 
    if not data: 
        break 
    ''' 
    这里使用的处理程序对待套接字通信就像是文件一样,所以必須发送行终止符(回车和换行符)。 
    而服务器只是保留并重用这里发送的终止符。 
    ''' 
    data = '%s/r/n' % data 
    tcpCliSock.send(data.encode('utf-8')) 
    resu = tcpCliSock.recv(BUFSIZ) 
    if not resu: 
        break 
    # 加strip()处理掉换行符 
    print(resu.decode('utf-8').strip()) 
    tcpCliSock.close() 
 
运行服务端和客户端后效果如下: 
client端: 
>>>百变星君 
[Wed May  9 20:44:11 2018] 百变星君 
>>>大圣娶亲 
[Wed May  9 20:44:22 2018] 大圣娶亲 
>>> 
server端: 
---------------------------------------- 
Exception happened during processing of request from ('127.0.0.1', 53286) 
---------------------------------------- 
...connected from: ('127.0.0.1', 53397) 
...connected from: ('127.0.0.1', 53398) 
...connected from: ('127.0.0.1', 53399) 
 
另一种不看成文件操作的支持并发连接的TCP时间戳服务器和客户端如下: 
socketserver TCP时间戳服务器 
#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
import socketserver 
from time import ctime 
 
# 并发编程 
class MyServer(socketserver.BaseRequestHandler): 
 
    def handle(self): 
        print('...连接来自:', self.client_address) 
        msg = self.request.recv(1024) 
        msg = '[%s] %s' % (ctime(), msg.decode('utf-8')) 
        print(msg) 
        self.request.send(msg.encode('utf-8')) 
 
if __name__ == '__main__': 
    # 支持重用端口和Ip 
    socketserver.TCPServer.allow_reuse_address = True 
    # ThreadingTCPServer 支持线程功能 
    server = socketserver.ThreadingTCPServer(('127.0.0.1', 9527), MyServer) 
    print('等待连接...') 
    server.serve_forever() 
 
socketserver TCP时间戳客户端 
#!/usr/bin/env python 
# _*_ coding: utf-8 _*_ 
 
import socket 
 
while True: 
    client = socket.socket() 
    client.connect(('127.0.0.1', 9527)) 
    data = input('>>>') 
    if not data: 
        break 
    client.send(data.encode('utf-8')) 
    resu = client.recv(1024) 
    if not resu: 
        break 
    print(resu.decode('utf-8')) 
    client.close() 
 
运行效果如下: 
第一个client端: 
>>>哆啦A梦 
[Wed May  9 20:59:14 2018] 哆啦A梦 
>>>蜡笔小新 
[Wed May  9 20:59:27 2018] 蜡笔小新 
>>>超人迪加 
[Wed May  9 21:00:13 2018] 超人迪加 
>>> 
第二个client端: 
>>>银河唯一的秘密 
[Wed May  9 20:59:50 2018] 银河唯一的秘密 
>>>护卫人类,挽救地球,看守这宇宙 
[Wed May  9 21:00:35 2018] 护卫人类,挽救地球,看守这宇宙 
>>> 
socketserver服务端: 
等待连接... 
...连接来自: ('127.0.0.1', 53505) 
[Wed May  9 20:59:14 2018] 哆啦A梦 
...连接来自: ('127.0.0.1', 53506) 
[Wed May  9 20:59:27 2018] 蜡笔小新 
...连接来自: ('127.0.0.1', 53507) 
...连接来自: ('127.0.0.1', 53508) 
[Wed May  9 20:59:50 2018] 银河唯一的秘密 
...连接来自: ('127.0.0.1', 53509) 
[Wed May  9 21:00:13 2018] 超人迪加 
...连接来自: ('127.0.0.1', 53510) 
[Wed May  9 21:00:35 2018] 护卫人类,挽救地球,看守这宇宙 
...连接来自: ('127.0.0.1', 53514)

end

参考:
http://www.cnblogs.com/Eva-J/
《python核心编程第四版》

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/industrynews/12746.html

(0)
上一篇 2021年7月19日 14:53
下一篇 2021年7月19日 14:53

相关推荐

发表回复

登录后才能评论