Chinaunix首页 | 论坛 | 博客
  • 博客访问: 235458
  • 博文数量: 32
  • 博客积分: 1971
  • 博客等级: 上尉
  • 技术积分: 390
  • 用 户 组: 普通用户
  • 注册时间: 2004-10-15 12:54
文章分类

全部博文(32)

文章存档

2012年(6)

2011年(19)

2009年(7)

分类: Python/Ruby

2011-05-19 15:15:55

研究了一段时间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
  1. #-*- coding:utf-8-*-
  2. '''
  3. Created on 2011-5-15

  4. @author: HAWK.Li
  5. '''
  6. import uuid
  7. import sys,os,time,subprocess
  8. import socket

  9. class Client():
  10.     """封装客户端核心方法
  11.     """
  12.     def login(self,tempid):
  13.         """登录"""
  14.         
  15.      
  16.         try:
  17.             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  18.             self.sock.connect((host, port))
  19.             msg = 'login '+tempid
  20.             self.sock.sendall(msg)
  21.             self.response = self.sock.recv(sockSize)
  22.             #分割字串,result[0]为标识,result[1]为返回结果
  23.             result = self.response.split(' ')
  24.             if result[0] == 'success':
  25.                 print "Login success! tempid(%s) is user(%s)!"%(tempid,result[1])
  26.                 print "connect close."
  27.                 global FLAG
  28.                 FLAG = False
  29.             elif result[0] == 'again':
  30.                 print "sleep 1 second and retry..."
  31.             elif result[0] == 'first':
  32.                 print "First Login..."
  33.             else:
  34.                 print "failed..."
  35.             #print self.response
  36.             return self.response
  37.         except:
  38.             print "登录出错!".decode('utf-8').encode("gbk")
  39.             print "Client.login Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])
  40.         
  41.     def finished(self,tempid):
  42.         """模拟成功的socket通知"""

  43.         try:
  44.             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  45.             self.sock.connect((host, port))
  46.             username = "Andy"
  47.             msg = 'finished '+tempid+" "+username
  48.             self.sock.sendall(msg)
  49.             self.response = self.sock.recv(sockSize)
  50.             #分割字串,result[0]为标识,result[1]为返回结果
  51.             result = self.response.split(' ')
  52.             print result
  53.         except:
  54.             print "模拟通知出错!".decode('utf-8').encode("gbk")
  55.             print "finished Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])
  56.         
  57.     def listid(self):
  58.         """列出服务器上的uuid和用户名的对应关系表"""
  59.         
  60.      
  61.         try:
  62.             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  63.             self.sock.connect((host, port))
  64.             msg = 'list '
  65.             self.sock.sendall(msg)
  66.             self.response = self.sock.recv(sockSize)
  67.             result = self.response
  68.             print result
  69.         except:
  70.             print "列表出错!".decode('utf-8').encode("gbk")
  71.             print "listid Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])

  72.     def closeSock(self):
  73.         self.sock.close()
  74.         

  75. if __name__ == "__main__":
  76.     global host,port,client,sockSize
  77.     sockSize = 64000
  78.     uid = str(uuid.uuid1())
  79.     host = '127.0.0.1'
  80.     port = int('1234')
  81.     
  82.     client = Client()
  83.     #这里的requestToken.py文件并不重要,我也没写
  84.     p = subprocess.Popen('"C:\Program Files\Internet Explorer\iexplore.exe" '+uid)

  85.     #退出标记
  86.     FLAG = True

  87.     if len(sys.argv) >= 2:
  88.         #登录成功的标记
  89.         if sys.argv[1] == 'finished':
  90.             existid = sys.argv[2]
  91.             r = client.finished(existid)
  92.         elif sys.argv[1] == 'list':
  93.             r = client.listid()
  94.     else:
  95.         print 'Send tempid('+uid+') to LoginServer...'
  96.         for i in range(120):
  97.             if FLAG:
  98.                 r = client.login(uid)
  99.                 time.sleep(1)
  100.             else:
  101.                 break

server.py
  1. #-*- coding:utf-8-*-
  2. '''
  3. Created on 2011-5-15

  4. @author: HAWK.Li
  5. '''
  6. import uuid
  7. import sys,os,time,subprocess
  8. import SocketServer,socket
  9. import time,datetime
  10. import threading


  11. class MyTCPHandler(SocketServer.StreamRequestHandler):
  12.     def handle(self):
  13.         uid = None

  14.         while True:
  15.             self.data = self.request.recv(sockSize)
  16.             cur_thread = threading.currentThread()
  17.             cmd = self.data
  18.             if cmd == None or len(cmd) == 0:
  19.                 break;
  20.             recvStr = cmd.split(' ')
  21.             #print recvStr
  22. # try:
  23.             if cmd.startswith('login'):
  24.                 firstflag = True
  25.                 uid = recvStr[1]
  26.                 print "RECV from ", self.client_address[0],uid
  27.                 for key in ids.keys():
  28.                     #检查登录的ID是否已经在列表中
  29.                     if key == uid:
  30.                         firstflag = False
  31.                         #检查登录的ID是否已经通过用户名验证,是None则告诉客户端还没验证通过
  32.                         if ids[key] == None:
  33.                             result = "again %s"%(uid)
  34.                         else:
  35.                             result = "success %s"%(ids[key])
  36.                         continue
  37.                 if firstflag:
  38.                     #说明这个客户端第一次登录
  39.                     result = "first %s"%(uid)
  40.                     username=None
  41.                     ids[uid] = username

  42.                 print result
  43.                 print ids
  44.                 self.request.sendall(result)
  45.             #收到用户名成功验证的通知
  46.             elif cmd.startswith('finished'):
  47.                 uid = recvStr[1]
  48.                 username = recvStr[2]
  49.                 ids[uid] = username
  50.                 self.request.sendall('OK')
  51.             elif cmd.startswith('list'):
  52.                 self.request.sendall(str(ids))
  53.             else:
  54.                 result = 'error'
  55. # self.wfile.write(result)
  56. # self.wfile.write('\n')
  57. # except:
  58. # print 'error'
  59.                 break
  60.         try:
  61.             if uid != None:
  62.                 ids.remove(uid)
  63.         except:
  64.             pass
  65.         #print uid, ' closed.'

  66. class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
  67.     #socket.setdefaulttimeout(5)
  68.     pass

  69. if __name__ == "__main__":
  70.     HOST = ''
  71.     PORT = int('1234')
  72.     sockSize = 64000
  73.     ids = {'id':None}
  74.     print 'login server start...'
  75.     server = ThreadedTCPServer((HOST, PORT), MyTCPHandler)
  76.     server_thread = threading.Thread(target=server.serve_forever)
  77.     server_thread.setDaemon(True)
  78.     server_thread.start()
  79.     server.serve_forever()

f.py
这个是一个CGI程序,需要使用python -m CGIHTTPServer方式运行,并放在cgi-bin目录下,给他执行权限
  1. #!/usr/bin/env python

  2. import cgi
  3. import uuid
  4. import sys,os,time,subprocess
  5. import socket

  6. #CGIAuth DEMO
  7. header = 'Content-Type: text/html\n\n'

  8. formhtml = '''<br></span></li><li>CGIAuth Demo
  9. USER



  10. Enter your uuid:


  11. USERNAME:

  12. %s
  13. '''


  14. fradio = ' %s\n'

  15. def showForm():
  16.     friends = ''
  17.     for i in [0, 10, 25, 50, 100]:
  18.         checked = ''
  19.         if i == 0:
  20.             checked = 'CHECKED'
  21.         friends = friends + fradio % \
  22.             (str(i), checked, str(i))

  23.     print header + formhtml % (friends)

  24. reshtml = '''<br></span></li><li>CGIAuth Demo
  25. Users : %s


  26. Your uuid is: %s


  27. Your username is: %s .
  28. '''

  29. def doResults(who, howmany):
  30.     print header + reshtml % (who, who, howmany)
  31.     global host,port,client,sockSize
  32.     sockSize = 64000
  33.     #uid = str(uuid.uuid1())
  34.     uid = who
  35.     host = '127.0.0.1'
  36.     port = int('1234')
  37.     client = Client()
  38.     users = howmany
  39.     r = client.finished(uid,users)
  40.     print r

  41. def process():
  42.     form = cgi.FieldStorage()
  43.     if form.has_key('person'):
  44.         who = form['person'].value
  45.     else:
  46.         who = 'NEW USER'

  47.     if form.has_key('howmany'):
  48.         howmany = form['howmany'].value
  49.     else:
  50.         howmany = 0

  51.     if form.has_key('action'):
  52.         doResults(who, howmany)
  53.     else:
  54.         showForm()

  55. class Client():
  56.         
  57.     def finished(self,tempid,username):
  58.         try:
  59.             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  60.             self.sock.connect((host, port))
  61.             msg = 'finished '+tempid+" "+username
  62.             self.sock.sendall(msg)
  63.             self.response = self.sock.recv(sockSize)
  64.             result = self.response.split(' ')
  65.             return result
  66.         except:
  67.             print "finished Error:"+str(sys.exc_info()[0])+str(sys.exc_info()[1])
  68.         

  69.     def closeSock(self):
  70.         self.sock.close()
  71.         

  72. if __name__ == '__main__':
  73.     process()






阅读(3104) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~