首先看下我的测试代码
from twisted.protocols.ftp import FTPFactory, FTPRealm
from twisted.cred.portal import Portal
from twisted.cred.checkers import AllowAnonymousAccess, FilePasswordDB
from twisted.internet import reactor
from twisted.internet import endpoints
#pass.dat the same directory with ftp01.py
# =====================
# jeff:bozo
# grimmtooth:bozo2
# =====================
#
p = Portal(FTPRealm(anonymousRoot='E:/', userHome='E:/'),
[AllowAnonymousAccess(), FilePasswordDB("pass.dat")])
f = FTPFactory(p)
endpoints.serverFromString(reactor,'tcp:21').listen(f)
reactor.run()
Portal类是cred的认证类,是用户登陆认证入口,通过Portal来构造一个FTPFactory提供TCP的服务(FTP,FTP是建立在TCP基础上的)。
FTPRealm是从BaseFTPRealm派生,BaseFTPRealm实现了portal.IRealm的接口。
class BaseFTPRealm:
"""
Base class for simple FTP realms which provides an easy hook for specifying
the home directory for each user.
"""
implements(portal.IRealm)
def __init__(self, anonymousRoot):
self.anonymousRoot = filepath.FilePath(anonymousRoot)
def getHomeDirectory(self, avatarId):
"""
Return a L{FilePath} representing the home directory of the given
avatar. Override this in a subclass.
@param avatarId: A user identifier returned from a credentials checker.
@type avatarId: C{str}
@rtype: L{FilePath}
"""
raise NotImplementedError(
"%r did not override getHomeDirectory" % (self.__class__,))
def requestAvatar(self, avatarId, mind, *interfaces):
for iface in interfaces:
if iface is IFTPShell:
if avatarId is checkers.ANONYMOUS:
avatar = FTPAnonymousShell(self.anonymousRoot)
else:
avatar = FTPShell(self.getHomeDirectory(avatarId))
return (IFTPShell, avatar,
getattr(avatar, 'logout', lambda: None))
raise NotImplementedError(
"Only IFTPShell interface is supported by this realm")
如果是匿名用户请求登陆则返回FTPAnonymousShell,否则返回FTPShell。这里FTPBaseRealm在用户登陆的时候直接以getHomeDirectory()作为FTP用户访问的根目录。
在看看FTPRealm,重写了getHomeDirectory,根据avatarId来返回一个子目录,这里特别要注意的是avatarId也就是FTP账号登陆的用户名。所以如果指定了userHome路径以后,还需要以用户名称来组织一个目录,这样才能让该用户正确访问到。
class FTPRealm(BaseFTPRealm):
"""
@type anonymousRoot: L{twisted.python.filepath.FilePath}
@ivar anonymousRoot: Root of the filesystem to which anonymous
users will be granted access.
@type userHome: L{filepath.FilePath}
@ivar userHome: Root of the filesystem containing user home directories.
"""
def __init__(self, anonymousRoot, userHome='/home'):
BaseFTPRealm.__init__(self, anonymousRoot)
self.userHome = filepath.FilePath(userHome)
def getHomeDirectory(self, avatarId):
"""
Use C{avatarId} as a single path segment to construct a child of
C{self.userHome} and return that child.
"""
return self.userHome.child(avatarId)
现在在反过来看看我例子里面的文件系统目录,匿名用户目录和用户目录都是E:盘,如果是匿名用户访问则直接访问E盘,否则如果是其他用户访问则访问的是E:\{用户名}目录。Portal的第二个参数指定了,用户的访问权限,可以匿名访问,而且可以通过文件密码的形式来访问,文件密码的格式(按照行),用户名:密码
[AllowAnonymousAccess(), FilePasswordDB("pass.dat")
这样通过几行代码就实现了一个简单的文件服务器了。也可以通过实现portal.IRealm接口来自定制访问权限。