zoukankan      html  css  js  c++  java
  • hashlib(hmac)进阶之client跟server交互

    首先我还是要强调不管任何相同的字符串通过hashlib加密之后都会产生相同的32位字符串,这个是日常Web中常用的加密算法如果我的client发送一个请求过来我server接受到后就要对该密码进行判断,正常的流程是我需要把client端发送过来的请求转换成hashlib格式的字符串跟我存在数据库中的字符串进行对比做判断,当前我也可以在这些字符串加密前加盐(key)

     

    以下是我的基础的server代码

    代码解析:

    首先我们需要明白Tcp下的服务端的7步曲

    1.实例化socket对象

    sk = socket.socket()

    2绑定对应的服务器IP地址以及对外开放的端口

    sk.bind(("127.0.0.1",8888))

    3启动监听等待用户访问

    sk.listen()

    4建立tcp下的基本连接协议

    conn,addr = sk.accept()

    #5通讯的逻辑

    rse = conn.recv(1024)#指定接收1024的字符串

    conn.send("相信你自己就是最牛逼的哪一个".decode("utf-8"))

    #6四次挥手断开连接

    conn.close()

    #7退还端口

    sk.close()

    这就是基础的服务端的代码

    下面我要对服务端进行改造改造代码如下

    # ### 服务器
    import socket
    import hmac
    import os
    
    def auth(conn,secret_key):
        # 随机的32位二进制字节流
        res = os.urandom(32)
        # 把res发送给对方的服务器
        conn.send(res)
        # 用hmac加密字符串
        hm = hmac.new(secret_key.encode(),res)
        # 通过hexdigest返回32长度的十六进制字符串
        res_server = hm.hexdigest()
        print(res_server)
        
        # 接受客户端发过来的加密后的字符串
        res_cli = conn.recv(1024).decode("utf-8")
        
        if res_server == res_cli:
            print("该用户是合法的链接请求")
            return True
        else:
            print("该用户是非法的链接请求")
            return False
    
    # 创建对象
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind( ("127.0.0.1",9001) )
    sk.listen()
    conn,addr = sk.accept()
    
    # 收发数据的逻辑
    secret_key = "ig不牛逼"
    # 调用函数,验证权限
    res = auth(conn,secret_key)
    # 如果验证的结果是真的,就处理用户发送过来的请求,假的直接断开链接
    if res:
        print(conn.recv(1024).decode("utf-8"))
    
    conn.close()
    sk.close()

    注os.urondom()是os模块下的一种方法,随机产生指定位数的随机2进制字节流

    此时我对收发逻辑做了一定的处理下面我为为我写的auth代码进行解析

    自定义一个auth权限函数,该函数需要2个变量conn(客户端返回服务端的端口对象或链接对象),secret_key(盐也就是我要key )

    1  在auth里面我随机生成了一个指定32位随机2进制字节流os.urandom(32)

    2 我需要把我服务端生成的32位随机季节流发送给服务端 conn.send(res)

    3 用hmac加密hmac.new(secret_key.encode(),res)  俩个参数(一个是我需要加入的盐,另外一个是我生成的32位随机2进制流)

    通过hmac的加密我会产生一个新的数据流 而这个数据流与客户端发送过来的数据流是一致的话那么我就可以认为此时的数据所对应的结果是一致的密码匹配成功

    4 接受客户端加密后的密码串进行反解

    5 验证客户端的加密是否与服务端的加密内容是否一致

    6返回判断的内容

     

    如果auth判断的结果是Ture那么我就直接给客户端返回这个登录状态登录成功

    否则则给客户端返回一个登录失败的状态

    关闭连接

    退还端口

    客户端的代码

    是首先需要对socket进行引入

    import socket

    sk = socket.socket()#实例化socket对象

    sk .connect(("127.0.0.1",8888))#指定相对应的服务器访问

    sk.send("相信你自己是最牛逼的哪一个".encode())#对服务器发送请求

    res = sk.recv(1024).decode("utf-8")接受服务端给我返回的消息

    sk.close()#退还端口

    下面我队我客户端的代码进行优化

    # ### 客户端
    import socket
    import hmac
    
    def auth(sk,secret_key):
        # 收发数据的逻辑
        msg = sk.recv(32)
        # 把key 和 msg 扔到new当中,进行加密
        hm = hmac.new(secret_key.encode(),msg)
        # 通过hexdigest把加密后的结果返回出来
        res_cli = hm.hexdigest()
        print(res_cli)
        # 把加密好的字符串在发送回原服务器
        sk.send(res_cli.encode())
        
        
    sk = socket.socket()
    sk.connect( ("127.0.0.1",9001) )
    
    
    secret_key = "ig不牛逼"
    # 调用函数,验证权限
    auth(sk,secret_key)
    # 向服务器发出下载的请求
    sk.send(b"download")
    
    
    
    
    # 关闭链接
    sk.close()

    首先基于原始的client与服务端的通信下我对我的收发数据的逻辑进行了改造

    自定义一个auth权限函数,该函数需要2个变量sk(客户端要与服务端的端口对象或链接对象),secret_key(盐也就是我要key )

    1 我需要把我客户端接受服务端给我发送的32位随机2进制流 sk.recv(32)

    3 用hmac加密hmac.new(secret_key.encode(),res)  俩个参数(一个是我需要加入的盐,另外一个是我生成的32位随机2进制流)

    通过hmac的加密我会产生一个新的数据流 而这个数据流我会发送给服务端数,如果我客户端收缩加密的字节流与服务端加密的是一致的话我会得到服务端的校验判定

     

    sk.close()#退还端口

     

     传统简易的server与client登录验证交互

    server代码

     

    基于上面的代码我现在扩展模拟到数据库中取出我的账号以及密码并且对服务端发送过来的账户密码进行验证,如果验证成功我就给客户端返回登录状态

     

    以下是代码

    # ### 服务端
    import hashlib
    import socket
    import json
    
    def get_md5_code(usr,pwd):
        # 将用户名作为加密的盐key
        hm = hashlib.md5(usr.encode())
        hm.update(pwd.encode())
        res = hm.hexdigest()
        return res
        
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind( ("127.0.0.1",9000) )
    sk.listen()
    conn,addr = sk.accept()
    
    # 处理收发数据的逻辑
    # 先接受二进制字节流 -> 字符串
    msg = conn.recv(1024).decode()
    print(msg,type(msg))
    # 把字符串 -> 字典
    dic = json.loads(msg)
    print(dic,type(dic))
    
    # 标识符,默认False
    sign = False
    with open("userinfo.txt",mode="r",encoding="utf-8") as fp:
        for line in fp:
            usr,pwd = line.strip().split(":")
            if usr == dic["username"] and pwd == get_md5_code(dic["username"],dic["password"]):
                # 登陆成功 返回状态吗为1
                res = {"code":1}
                res_msg = json.dumps(res).encode()
                conn.send(res_msg)
                # 如果登录成功,默认改成True
                sign = True
                break
                
    if sign == False:
        #登录失败,返回状态码为0
        res = {"code":0}
        conn.send(json.dumps(res).encode())
                
                
    
    # 四次挥手
    conn.close()
    # 退还端口
    sk.close()

    代码解析

    def get_md5_code(usr,pwd):
        # 将用户名作为加密的盐key
        hm = hashlib.md5(usr.encode())
        hm.update(pwd.encode())
        res = hm.hexdigest()
        return res
    这里代码的主要作用是把用户名作为盐(key),pwd作为要加密的字符串
    返回的是一个加盐(key)后的随机32位字符串,当我收到客户端的数据后需要经过json 字符串的反解后得到的一个字典,该字典包含了密码以及用户名的一种数据
    此时我需要把字典的用户名以及密码拿出来做加密后跟数据库所存的随机字符串做比对,如果比对成功那么我就会给客户端返回一个状态反之给客户端返回一个否定的状态
    下面我要介绍我的客户端的代码

    # ### 客户端
    import socket
    import json
    sk = socket.socket()
    sk.connect( ("127.0.0.1",9000) )
    
    usr = input("请输入您的账户:")
    pwd = input("请输入您的密码:")
    dic = {"username":usr,"password":pwd,"operate":"login"}
    # 返回json格式的字符串
    res = json.dumps(dic)
    # 在把json字符串转换成字节流
    bytes_msg = res.encode()
    # 发送给服务端
    sk.send(bytes_msg)
    
    
    # 接受服务端发送回来的数据
    res_msg = sk.recv(1024).decode()
    # 把字符串变成字典
    dic_code = json.loads(res_msg)
    
    
    if dic_code["code"]:
        print("恭喜你,登陆成功")
    else:
        print("抱歉,登陆失败")
    
    
    
    
    # 关闭链接
    sk.close()

    首先在客户端中我要生成一个字典,字典里面包含了我的账户以及密码名我通过json下的dumps强制把字典转换为json类型的字符串,通过这个字符串我直接发给服务端,让服务端帮我操作,如果匹配成功那么我就返回一个已经登录的状态,反之登录失败





     

     

     

  • 相关阅读:
    Codeforces 765 E. Tree Folding
    Codeforces 617 E. XOR and Favorite Number
    2017.3.4[hihocoder#1403]后缀数组一·重复旋律
    2017.2.23[hdu1814]Peaceful Commission(2-SAT)
    2017.2.18Codeforces Round #398 (Div. 2)
    2017.2.18[codevs1170]NOIP2008提高组复赛T4双栈排序
    2017.2.18[codevs3319][bzoj3670]NOI2014D2T1动物园
    2017.2.18[codevs3311][bzoj3668]NOI2014D1T1起床困难综合症
    2017.2.10 Splay总结
    2017.2.10考试总结2017冬令营
  • 原文地址:https://www.cnblogs.com/zyling/p/11843449.html
Copyright © 2011-2022 走看看