.crossbar 平级目录中添加 authenticator.py 用来操作 crossbar 的认证, 客户端 crossbar 连接输入的用户名密码在这个文件里进行动态认证
from pprint import pprint from twisted.internet.defer import inlineCallbacks from autobahn.twisted.wamp import ApplicationSession from autobahn.wamp.exception import ApplicationError # crossbar "database" USERDB = { 'frontend': { # 用户名 'secret': '123456', # 密码 'role': 'frontend' # 角色 }, 'backend': { 'authid': 'ID10001', 'secret': '111111', 'role': 'backend' } } class AuthenticatorSession(ApplicationSession): @inlineCallbacks def onJoin(self, details): def authenticate(realm, authid, details): print("WAMP-CRA dynamic authenticator invoked: realm='{}', authid='{}'".format(realm, authid)) if authid in USERDB: return USERDB[authid] else: raise ApplicationError(u'com.example.no_such_user', 'could not authenticate session - no such user {}'.format(authid)) try: yield self.register(authenticate, u'com.example.authenticate') print("WAMP-CRA dynamic authenticator registered!") except Exception as e: print("Failed to register dynamic authenticator: {0}".format(e))
修改 .crossbar 文件夹下的 config.json 文件, 默认是 anonymous 配置,再 roles 节点里面修改,添加 authenticator 认证管理 e.g.
... "realms": [ { "name": "realm1", "roles": [ { "name": "authenticator", "permissions": [ { "uri": "com.example.authenticate", "match": "exact", "allow": { "call": false, "register": true, "publish": false, "subscribe": false }, "disclose": { "caller": false, "publisher": false }, "cache": true } ] }, { "name": "backend", "permissions": [ { "uri": "", "match": "prefix", "allow": { "call": true, "register": true, "publish": true, "subscribe": true }, "disclose": { "caller": false, "publisher": false }, "cache": true } ] }, { "name": "frontend", "permissions": [ { "uri": "com.example.add2", "match": "exact", "allow": { "call": true, "register": false, "publish": false, "subscribe": false }, "disclose": { "caller": false, "publisher": false }, "cache": true } ] } ] } ], "transports": [ { "type": "web", "endpoint": { "type": "tcp", "port": 8080 }, "paths": { "/": { "type": "static", "directory": "../web" }, "ws": { "type": "websocket", "auth": { "wampcra": { "type": "dynamic", "authenticator": "com.example.authenticate" } } } } } ], "components": [ { "type": "class", "classname": "authenticator.AuthenticatorSession", "realm": "realm1", "role": "authenticator" } ] ...
"name": "backend" 角色为后端,配置权限 "match": "prefix" 设置 uri 的匹配规则。 prefix matching 前缀匹配
{ "name": "backend", "permissions": [ { "uri": "", "match": "prefix", "allow": { "call": true, "register": true, "publish": true, "subscribe": true }, "disclose": { "caller": false, "publisher": false }, "cache": true } ] }
设置 websocket 认证方式, auth 中配置 wampcra 模式,类型为 dynamic
"ws": { "type": "websocket", "auth": { "wampcra": { "type": "dynamic", "authenticator": "com.example.authenticate" } } }
"name" : "authenticator" 启动crossbar 挂载的 component
"components": [ { "type": "class", "classname": "authenticator.AuthenticatorSession", "realm": "realm1", "role": "authenticator" } ]
crossbar 客户端连接添加user和key backend 角色 python 例子 e.g.
import os import sys from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks from autobahn.twisted.wamp import ApplicationSession from autobahn.wamp.types import PublishOptions from autobahn.wamp import auth USER = u'backend' USER_SECRET = u'111111' class ClientSession(ApplicationSession): def onConnect(self): self.join(self.config.realm, [u"wampcra"], USER) def onChallenge(self, challenge): if challenge.method == u"wampcra": if u'salt' in challenge.extra: key = auth.derive_key(USER_SECRET, challenge.extra['salt'], challenge.extra['iterations'], challenge.extra['keylen']) else: key = USER_SECRET signature = auth.compute_wcs(key, challenge.extra['challenge']) return signature else: raise Exception("Invalid authmethod {}".format(challenge.method)) @inlineCallbacks def onJoin(self, details): def add2(x, y): print("add2() called with {} and {}".format(x, y)) return x + y try: reg = yield self.register(add2, u'com.example.add2') print("procedure add2() registered") except Exception as e: print("could not register procedure: {}".format(e)) try: reg = yield self.register(add2, u'com.example.wewobackend.test') print("wewobackend.test registered") except Exception as e: print("wewobackend.test could not register procedure: {}".format(e)) def onLeave(self, details): print("Client session left: {}".format(details)) self.disconnect() def onDisconnect(self): reactor.stop() if __name__ == '__main__': from autobahn.twisted.wamp import ApplicationRunner runner = ApplicationRunner(url=u'ws://localhost:8080/ws', realm=u'realm1') runner.run(ClientSession)