鐵樂學Python_Day35_Socket模塊3和hmac模塊

来源:https://www.cnblogs.com/tielemao/archive/2018/05/11/9022939.html
-Advertisement-
Play Games

驗證客戶端鏈接的合法性 如果你想在分散式系統中實現一個簡單的客戶端鏈接認證功能,又不像SSL那麼複雜, 那麼可以利用hmac+加鹽的方式來實現。 SocketServer是標準庫中的一個高級模塊(python3.x中重命名為socketserver), 它的目標是簡化很多樣板代碼,它們是創建網路客... ...


驗證客戶端鏈接的合法性

如果你想在分散式系統中實現一個簡單的客戶端鏈接認證功能,又不像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核心編程第四版》


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 模板模式和鉤子函數 一.什麼是模板模式? 定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中,模板方法使得子類可以不改變一個演算法的結構就能重新定義該演算法的某些特定步驟,模板模式屬於設計模式中的行為模式。 二.如何實現模板模式? 三.模板模式的實現要素? 準備一個抽象基類,將部分邏輯以具體方法的形式 ...
  • 參加工作 關於從學生到職場的轉變與心態起伏,已然有許多文筆好的朋友感嘆過,我想自己作為一個平凡的人,相較他人也不會有更為特別的感受,自然也就不值當多說。只簡單聊聊自身的情況,1月份畢業於非電腦專業,3月底檔案上的職業欄從 變成了 手動滑稽,編程功底相比電腦專業的各位前輩有較大差距,但這就是我職業 ...
  • 我們成功書寫了HelloWorld後,又深入瞭解了main函數,提到過main並非是關鍵字,可什麼又是關鍵字呢?這其實就是這章要研究的內容,本節研究關鍵字與標識符,在標識符中我們也會講解一下Java中的駝峰命名; 1.1 關鍵字 在Hello World中,我們發現其中有很多單詞是固定的,這其實就是 ...
  • 最近也是挺煩的,博客園做為程式員的家園,其實不假。雖然現在寫出的隨筆,看的人少。就當自娛自樂了。煩惱就是矛盾引起的,人很多想法都會被外界環境影響。比如一個思考很久的決定,當事情真發生時,考慮過多,受到外界環境的影響就改變了。 面試真的靠技巧,雖然你很NB,但是面試官不知道,不給你機會,也是白搭。真正 ...
  • JSPs /admin/* BASIC ...
  • 單體應用架構 架構總感覺理我很遠,有時候感覺很迷茫。今天起我把我認識到的三種架構寫出來,一是希望沉澱一下自己所學的東西,二是希望有人能指出我的不足指出,向大家學習。 第一篇 單體應用架構我會總結出單體架構的優缺點,和一般我在經歷過的項目中單體架構所用到的技術,以及我需掌握的知識。 第二篇 垂直應用架 ...
  • 課程是按照真實企業級開發項目流程進行講解,通過學習此課程可以體會到真實的大型大數據項目開發流程,學完此課程可以熟練掌握大數據技術,java web技術,docker虛擬化技術,分散式技術,緩存技術,linux等。 ...
  • 雙擊 勾上藍色保存 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...