最近在看《Twisted Network Programming Essentials 2nd》一书,网上找不到中文版的(好像第一版被翻译了部分,第二版好像没被翻译),哭瞎。里面有讲cred的。
各个重要的参数说明一下:http://twistedmatrix.com/documents/13.1.0/core/howto/cred.html#auto2这里有很详细说明哦。
Credentials:与验证用户的信息有关。最常用的就是记录着用户名和密码。(我喜欢把它叫做一个装有你的用户名密码的容器。)实现了twisted.cred.credentials.ICredentials
Avatar:你验证成功后的代表着用户的一个对象。
AvatarID:这个用来表明用户的身份的。在本例子里,就是user
Credentials checker:这个是用验证你的Credentials是否合法。如果合法的话,会返回一个avatarID。例子中的InMemoryUsernamePasswordDatabaseDontUse()这是一个CredentialsChecker
Realm:为所有用户定义一些操作。在Realm的requestAvatar方法中,会传入一个avatarId,可以根据不同的用户来规定不同的权限。它会返回一个avatar对象
Portal:把一系列的CredentialsChecker集中起来。主要完成的就是把Credentials与Credentials checker间的信息进行核对。portal的login方法,会返回一个Deferred,注册回调checker的requestAvatarId,这会返回一个AvatarID,然后再注册回调一个Realm的requestAvatar。
下面代码,实现了一个需要用户凭证的echo服务器,使用user pass 来验证身份。
#!/usr/bin/env python #coding:utf8 # Author : tuxpy # Email : [email protected] # Last modified : 2014-08-25 15:35:35 # Filename : tw_cred.py # Description : from zope.interface import implements,Interface from twisted.cred import checkers,credentials,portal from twisted.internet import protocol,reactor from twisted.protocols import basic class IProtocolAvatar(Interface): def logout(): """ clean up per-login resources ollocated to this avator. """ class EchoAvatar(object): implements(IProtocolAvatar) def logout(self): pass class Echo(basic.LineReceiver): protal=None avatar=None logout=None delimiter='\n' def cocnnectionLost(self,reason): if self.logout: self.logout() self.avater=None seslf.logout=None def lineReceived(self,line): if not self.avatar: username,password=line.strip().split(" ") self.tryLogin(username,password) else: self.sendLine(line) def tryLogin(self,username,password): self.portal.login(credentials.UsernamePassword(username, password), None,IProtocolAvatar).addCallbacks(self._cbLogin, self._ebLogin) def _cbLogin(self,(interface,avatar,logout)): self.avatar=avatar self.logout=logout self.sendLine("Login successful,please procees.") def _ebLogin(self,failure): self.sendLine("Login denied,goodbye.") self.transport.loseConnection() class EchoFactory(protocol.Factory): def __init__(self,portal): self.portal=portal def buildProtocol(self,addr): protocol=Echo() protocol.portal=self.portal return protocol class Realm(object): implements(portal.IRealm) def requestAvatar(self,avatarId,mind,*interfaces): for iface in interfaces: if iface is IProtocolAvatar: avatar=EchoAvatar() return IProtocolAvatar,avatar,avatar.logout raise NotImplementedError("This realm only supports the IProtocolAvatar interface.") realm=Realm() myPortal=portal.Portal(realm) checker=checkers.InMemoryUsernamePasswordDatabaseDontUse() checker.addUser("user","pass") myPortal.registerChecker(checker) reactor.listenTCP(1234,EchoFactory(myPortal)) reactor.run()
下面这图,很说明问题,看懂了,就基本上明白这几个
1.先是调用portal.login方法,将Credentials(UsernamePassword),mind(通常是None),interfaces(IProtocolAvatar) 当作为参数传入。
2.把Credentials与portal中已经注册了的checker(Credentials Checkers,使用portal的rergisterChecker(checker)注册)进行信息的匹配。
3.匹配无误,就会返回一个avatarId.
4.avatarId返回,激活回调函数self.realm.requestAvatar。
5.返回符合条件的avatar
6.返回avatar和logout方法
这还有一张twisted how to 上的图:
一些源码:
class Portal:
"""
A mediator between clients and a realm.
A portal is associated with one Realm and zero or more credentials checkers.
When a login is attempted, the portal finds the appropriate credentials
checker for the credentials given, invokes it, and if the credentials are
valid, retrieves the appropriate avatar from the Realm.
This class is not intended to be subclassed. Customization should be done
in the realm object and in the credentials checker objects.
"""
def __init__(self, realm, checkers=()):
"""
Create a Portal to a L{IRealm}.
"""
self.realm = realm
self.checkers = {}
for checker in checkers:
self.registerChecker(checker)
def listCredentialsInterfaces(self):
"""
Return list of credentials interfaces that can be used to login.
"""
return self.checkers.keys()
def registerChecker(self, checker, *credentialInterfaces):
if not credentialInterfaces:
credentialInterfaces = checker.credentialInterfaces
for credentialInterface in credentialInterfaces:
self.checkers[credentialInterface] = checker
def login(self, credentials, mind, *interfaces):
"""
@param credentials: an implementor of
L{twisted.cred.credentials.ICredentials}
@param mind: an object which implements a client-side interface for
your particular realm. In many cases, this may be None, so if the
word 'mind' confuses you, just ignore it.
@param interfaces: list of interfaces for the perspective that the mind
wishes to attach to. Usually, this will be only one interface, for
example IMailAccount. For highly dynamic protocols, however, this
may be a list like (IMailAccount, IUserChooser, IServiceInfo). To
expand: if we are speaking to the system over IMAP, any information
that will be relayed to the user MUST be returned as an
IMailAccount implementor; IMAP clients would not be able to
understand anything else. Any information about unusual status
would have to be relayed as a single mail message in an
otherwise-empty mailbox. However, in a web-based mail system, or a
PB-based client, the ``mind'' object inside the web server
(implemented with a dynamic page-viewing mechanism such as a
Twisted Web Resource) or on the user's client program may be
intelligent enough to respond to several ``server''-side
interfaces.
@return : A deferred which will fire a tuple of (interface,
avatarAspect, logout). The interface will be one of the interfaces
passed in the 'interfaces' argument. The 'avatarAspect' will
implement that interface. The 'logout' object is a callable which
will detach the mind from the avatar. It must be called when the
user has conceptually disconnected from the service. Although in
some cases this will not be in connectionLost (such as in a
web-based session), it will always be at the end of a user's
interactive session.
"""
for i in self.checkers:
if i.providedBy(credentials):
return maybeDeferred(self.checkers[i].requestAvatarId, credentials
).addCallback(self.realm.requestAvatar, mind, *interfaces
)
ifac = providedBy(credentials)
return defer.fail(failure.Failure(error.UnhandledCredentials(
"No checker for %s" % ', '.join(map(reflect.qual, ifac)))))
class InMemoryUsernamePasswordDatabaseDontUse:
"""
An extremely simple credentials checker.
This is only of use in one-off test programs or examples which don't
want to focus too much on how credentials are verified.
You really don't want to use this for anything else. It is, at best, a
toy. If you need a simple credentials checker for a real application,
see L{FilePasswordDB}.
"""
implements(ICredentialsChecker)
credentialInterfaces = (credentials.IUsernamePassword,
credentials.IUsernameHashedPassword)
def __init__(self, **users):
self.users = users
def addUser(self, username, password):
self.users[username] = password
def _cbPasswordMatch(self, matched, username):
if matched:
return username
else:
return failure.Failure(error.UnauthorizedLogin())
def requestAvatarId(self, credentials):
if credentials.username in self.users:
return defer.maybeDeferred(
credentials.checkPassword,
self.users[credentials.username]).addCallback(
self._cbPasswordMatch, str(credentials.username))
else:
return defer.fail(error.UnauthorizedLogin())