Crossbar.io 提供了一系列的认证方法。
- WAMP-Anonymouse
- WAMP-Ticket
- WAMP-CRA
- WAMP-Cryptosign
- WAMP-Cookie
- WAMP-TLS
项目中目前用到了会话相关的 Anonymous 、Ticket、 CRA 三种方式。
后端使用静态 Ticket ,前端普通用户使用 Anonymous 连接注册用户信息并保存到数据中,然后使用 CRA 动态认证通过查询数据库中的信息进行对比。
思路清晰后,开始一项项进行:
静态 Ticket 实现
修改 crossbar 运行项目的配置文件 .crossbar/config.json
如下,添加 backend
角色,可以进行注册调用,使用 prefix
匹配 uri
{
...
"workers": [
{
...
"realms": [
{
"name": "realm1",
"roles": [
{
"name": "backend",
"permissions": [
{
"uri": "",
"match": "prefix",
"allow": {
"call": true,
"register": true,
"publish": false,
"subscribe": false
},
"disclose": {
"caller": false,
"publisher": false
},
"cache": true
}
]
},
...
ws
属性设置 role
和 ticket
, 这样配置文件就搞定了
...
"transports": [
{
"type": "web",
"endpoint": {
"type": "tcp",
"port": 8080
},
"paths": {
"/": {
"type": "static",
"directory": "../web"
},
"ws": {
"type": "websocket",
"serializers": [
"json"
],
"auth": {
"ticket": {
"type": "static",
"principals": {
"backend": {
"ticket": "123456",
"role": "backend"
}
}
}
}
...
后端使用 Python
, 文件中添加 role
和 ticket
信息,在 self.join
中添加对应的 ticket
和 ROLE
参数,onChanllenge
中进行类型判断返回 ticket
,认证通过后会执行 onJoin
里面的内容。
...
ROLE = u'backend'
TICKET = u'123456'
class ClientSession(ApplicationSession):
def onConnect(self):
self.join(self.config.realm, [u"ticket"], ROLE)
def onChallenge(self, challenge):
if challenge.method == u"ticket":
return TICKET
else:
raise Exception("Invalid authmethod {}".format(challenge.method))
@inlineCallbacks
def onJoin(self, details):
def hi():
return 'hi'
try:
reg2 = yield self.register(hi, u'com.example.frontend.hi')
print("com.example.frontend.hi")
except Exception as e:
print("could not register procedure: {}".format(e))
...
后端静态 Ticket 连接搞定了,开始做前端的相关认证,先从匿名注册用户开始。
Anonymous 注册用户
回到 crossbar 的配置文件中 , 添加一个public
角色,只能调用一个匿名注册函数 com.example.anonymous.frontend.register
, 使用 exact
(完全匹配) 的方式,设置该角色只能执行 call
,后端程序注册好函数后,这样就提供了一个匿名用户只能注册的接口。
后端提供的这个接口连接数据库,把用户信息保存到数据库中,CRA 动态认证的时候使用注册的用户名密码等进行登录认证。
...
"realms": [
{
"name": "realm1",
"roles": [
...
{
"name": "public",
"permissions": [
{
"uri": "com.example.anonymous.frontend.register",
"match": "exact",
"allow": {
"call": true,
"register": false,
"publish": false,
"subscribe": false
},
"disclose": {
"caller": false,
"publisher": false
},
"cache": true
}
]
}
...
WAMP-CRA 动态认证
这一部分的实现思路是单独写一个基于 crossbar 连接的 component ,使用静态 Ticket 实现连接,在认证函数中通过前端登录页面输入的用户信息连接数据库进行对比。
再次操作 crossbar 的配置文件,添加一个 frontend
前端角色,使用 wildcard
(通配符匹配)的方式匹配 uri
,前端角色只能执行 call
。
添加一个 authenticator
认证角色,使用 exact
匹配 uri
,所有 crossbar 动态 CRA 认证连接通过 com.example.authenticate
接口来控制。
...
"realms": [
{
"name": "realm1",
"roles": [
{
"name": "frontend",
"permissions": [
{
"uri": "com.example.frontend.",
"match": "wildcard",
"allow": {
"call": true,
"register": false,
"publish": false,
"subscribe": false
},
"disclose": {
"caller": false,
"publisher": false
},
"cache": true
}
]
},
{
"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
}
]
}
...
使用 Python 实现认证 Component , 认证组件独立运行在服务端,使用静态 Ticket 连接,注册一个 com.example.authenticate
接口进行动态验证,调用接收到的用户信息 authenid
匹配数据库信息,用户名密码正确,认证通过。
...
ROLE = u'backend'
TICKET = u'sg-ai.com'
class ClientSession(ApplicationSession):
def onConnect(self):
self.join(self.config.realm, [u"ticket"], ROLE)
def onChallenge(self, challenge):
if challenge.method == u"ticket":
return TICKET
else:
raise Exception("Invalid authmethod {}".format(challenge.method))
async def onJoin(self, details):
async def authenticate(realm, authid, details):
try:
data = {'userName': authid}
res = await self.call(u'com.db.mongo.backend.select', data, 'user')
result = {'role': 'frontend', 'secret': res[0]['password']}
return result
except Exception as e:
print("authenticate function error: {0}".format(e))
try:
await self.register(authenticate, u'com.example.authenticate')
except Exception as e:
print("Failed to register dynamic authenticator: {0}".format(e))
def onLeave(self, details):
print("Client session left: {}".format(details))
self.disconnect()
def onDisconnect(self):
print("Client session disconnected.")
...
目前前端实现使用两套连接,第一次使用匿名方式连接,注册成功后连接关闭,然后在登录操作中使用CRA的动态认证连接方式。(不知道还有没有更好的实现方式)
注意
crossbar配置文件中 uri 中不能含有 -
字符
添加混合认证后提供给前端的接口 uri 中不能含有大写字母,不然会认证错误
Error {
error: 'wamp.error.not_authorized',
args:
[ 'session is not authorized to call procedure \'com.ai.user.frontend.getUserInfo\'' ],
kwargs: {} }
版本信息: js "autobahn": "^17.5.2"
Crossbar.io COMMUNITY 17.12.1
有任何疑问欢迎留言交流 ^ - ^