zl程序教程

您现在的位置是:首页 >  后端

当前栏目

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

Python模块编程语言 详解 socket HMAC
2023-06-13 09:20:37 时间
验证客户端链接的合法性

如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像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核心编程第四版》

12746.html

cmacpython