python cs架构_CS架构下OAuth认证的实现-附DEMO

研究了一段时间OAuth,在SNS网站之间,OAuth用的很多,手机类应用也不少,但是在桌面程序中很少有使用到OAuth,至于CS架构的程序,至少我是没发现这样的应用了。

新浪的OAuth认证中,有提出一种桌面应用的OAuth认证实现,其原理是使用PIN码,即用户在登录成功之后记录下新浪反馈的一个PIN码,然后在客户端中输入这个PIN值,再用这个进行access token等后续的验证,这个方案相当的搓,试想,一个桌面应用程序,在登录完之后,再要输入一个PIN码,太不方便了,用户体验很差。

我研究了较长时间,最终设计出一个方案,可以实现CS架构模型下使用OAuth认证,这个架构有点一小复杂,也不是一般的程序能用的上的,但这个思路也许能给看本文的人一个思路。

在我的实现中,客户端程序发起登录请求,发起请求时产生一个唯一ID,请注意这个ID必须是绝对唯一的,我的DEMO中使用了UUID代替,我认为对UUID+特定字串做MD5处理,应该能达到要求。然后将这个唯一ID传给server端,同时打开一个IE界面,也将这个唯一ID传递给负责进行OAuth认证的我方网站程序。

server端在收到唯一ID后,保持和客户端的通讯,并可以不做反馈(在我的DEMO中为了简便,设计为客户端每1秒钟发起一次查询请求,server端收到后检查是否通过认证,并立即返回)

我方网站程序是核心,在接收到唯一ID后,构造request Token,向第三方网站发起Oauth认证,引导用户IE浏览器跳转至第三方网站,并最终令用户成功在第三方网站登录完成。

在我方网站最终登录完成之后作access token和唯一ID、网站用户名的绑定关系,并通过socket将唯一ID和网站用户名的对应关系发送给server端,server端在接收到这个信息后更新状态,并通知客户端此用户已经成功登录,即可以走后续业务流程了。

这个思路其实并不复杂,但走的路线比较曲折,附件是我用python写的DEMO,思路就是如上,client.py是模拟了一个客户端请求,其中也实现了模拟发成功登录请求的模块;server.py是模拟接入服务端;p.py是模拟网站程序,在成功OAuth认证完成之后向server.py发送成功登录的socket请求。

请注意p.py需要使用python -m CGIHTTPServer方式运行的,不懂的人自行看python的书吧。

本文中的代码如何运行其实并不重要,重要的是思路,程序我在python2.7,linux/windows平台下都测试通过了。

不熟悉本文的OAuth协议的,请自行google。

client.py

#-*- coding:utf-8-*-

'''

Created on 2011-5-15

@author: HAWK.Li

'''

import uuid

import sys,os,time,subprocess

import socket

class Client():

"""封装客户端核心方法

"""

def login(self,tempid):

"""登录"""

try:

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

self.sock.connect((host, port))

msg = 'login '+tempid

self.sock.sendall(msg)

self.response = self.sock.recv(sockSize)

#分割字串,result[0]为标识,result[1]为返回结果

result = self.response.split(' ')

if result[0] == 'success':

print "Login success! tempid(%s) is user(%s)!"%(tempid,result[1])

print "connect close."

global FLAG

FLAG = False

elif result[0] == 'again':

print "sleep 1 second and retry..."

elif result[0] == 'first':

print "First Login..."

else:

print "failed..."

#print self.response

return self.response

except:

print "登录出错!".decode('utf-8').encode("gbk")

print "Client.login Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])

def finished(self,tempid):

"""模拟成功的socket通知"""

try:

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

self.sock.connect((host, port))

username = "Andy"

msg = 'finished '+tempid+" "+username

self.sock.sendall(msg)

self.response = self.sock.recv(sockSize)

#分割字串,result[0]为标识,result[1]为返回结果

result = self.response.split(' ')

print result

except:

print "模拟通知出错!".decode('utf-8').encode("gbk")

print "finished Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])

def listid(self):

"""列出服务器上的uuid和用户名的对应关系表"""

try:

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

self.sock.connect((host, port))

msg = 'list '

self.sock.sendall(msg)

self.response = self.sock.recv(sockSize)

result = self.response

print result

except:

print "列表出错!".decode('utf-8').encode("gbk")

print "listid Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])

def closeSock(self):

self.sock.close()

if __name__ == "__main__":

global host,port,client,sockSize

sockSize = 64000

uid = str(uuid.uuid1())

host = '127.0.0.1'

port = int('1234')

client = Client()

#这里的requestToken.py文件并不重要,我也没写

p = subprocess.Popen('"C:\Program Files\Internet Explorer\iexplore.exe" http://127.0.0.1/requestToken.py?uuid='+uid)

#退出标记

FLAG = True

if len(sys.argv) >= 2:

#登录成功的标记

if sys.argv[1] == 'finished':

existid = sys.argv[2]

r = client.finished(existid)

elif sys.argv[1] == 'list':

r = client.listid()

else:

print 'Send tempid('+uid+') to LoginServer...'

for i in range(120):

if FLAG:

r = client.login(uid)

time.sleep(1)

else:

break

server.py

#-*- coding:utf-8-*-

'''

Created on 2011-5-15

@author: HAWK.Li

'''

import uuid

import sys,os,time,subprocess

import SocketServer,socket

import time,datetime

import threading

class MyTCPHandler(SocketServer.StreamRequestHandler):

def handle(self):

uid = None

while True:

self.data = self.request.recv(sockSize)

cur_thread = threading.currentThread()

cmd = self.data

if cmd == None or len(cmd) == 0:

break;

recvStr = cmd.split(' ')

#print recvStr

# try:

if cmd.startswith('login'):

firstflag = True

uid = recvStr[1]

print "RECV from ", self.client_address[0],uid

for key in ids.keys():

#检查登录的ID是否已经在列表中

if key == uid:

firstflag = False

#检查登录的ID是否已经通过用户名验证,是None则告诉客户端还没验证通过

if ids[key] == None:

result = "again %s"%(uid)

else:

result = "success %s"%(ids[key])

continue

if firstflag:

#说明这个客户端第一次登录

result = "first %s"%(uid)

username=None

ids[uid] = username

print result

print ids

self.request.sendall(result)

#收到用户名成功验证的通知

elif cmd.startswith('finished'):

uid = recvStr[1]

username = recvStr[2]

ids[uid] = username

self.request.sendall('OK')

elif cmd.startswith('list'):

self.request.sendall(str(ids))

else:

result = 'error'

# self.wfile.write(result)

# self.wfile.write('\n')

# except:

# print 'error'

break

try:

if uid != None:

ids.remove(uid)

except:

pass

#print uid, ' closed.'

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):

#socket.setdefaulttimeout(5)

pass

if __name__ == "__main__":

HOST = ''

PORT = int('1234')

sockSize = 64000

ids = {'id':None}

print 'login server start...'

server = ThreadedTCPServer((HOST, PORT), MyTCPHandler)

server_thread = threading.Thread(target=server.serve_forever)

server_thread.setDaemon(True)

server_thread.start()

server.serve_forever()

f.py

这个是一个CGI程序,需要使用python -m CGIHTTPServer方式运行,并放在cgi-bin目录下,给他执行权限

#!/usr/bin/env python

import cgi

import uuid

import sys,os,time,subprocess

import socket

#CGIAuth DEMO

header = 'Content-Type: text/html\n\n'

formhtml = '''

CGIAuth Demo

USER

Enter your uuid:

USERNAME:

%s

'''

fradio = ' %s\n'

def showForm():

friends = ''

for i in [0, 10, 25, 50, 100]:

checked = ''

if i == 0:

checked = 'CHECKED'

friends = friends + fradio % \

(str(i), checked, str(i))

print header + formhtml % (friends)

reshtml = '''

CGIAuth Demo

Users : %s

Your uuid is: %s

Your username is: %s .

'''

def doResults(who, howmany):

print header + reshtml % (who, who, howmany)

global host,port,client,sockSize

sockSize = 64000

#uid = str(uuid.uuid1())

uid = who

host = '127.0.0.1'

port = int('1234')

client = Client()

users = howmany

r = client.finished(uid,users)

print r

def process():

form = cgi.FieldStorage()

if form.has_key('person'):

who = form['person'].value

else:

who = 'NEW USER'

if form.has_key('howmany'):

howmany = form['howmany'].value

else:

howmany = 0

if form.has_key('action'):

doResults(who, howmany)

else:

showForm()

class Client():

def finished(self,tempid,username):

try:

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

self.sock.connect((host, port))

msg = 'finished '+tempid+" "+username

self.sock.sendall(msg)

self.response = self.sock.recv(sockSize)

result = self.response.split(' ')

return result

except:

print "finished Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])

def closeSock(self):

self.sock.close()

if __name__ == '__main__':

process()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值