版主你好。我用的是这种方式,https://github.com/contentblvd/nodebb-plugin-sso-session/blob/master/library.js
重写了continueLogin,逻辑写在request的回调中。
request('http://mydomain.com/users/' + userToken, function (err, response, body) {xxx})。
-
NodeBB 自定义sso插件问题请教
-
river |
你不帖代码还是无法看出你的问题, 你看看 nodebb-plugin-sso-session 的逻辑:
plugin.continueLogin = function(req, username, password, next) { ...
注意那个 next 回调, 是在调用 moreLogin 之后才调用的:
plugin.moreLogin({ CBid: profile.id, handle: profile.displayName, email: profile.email, isAdmin: profile.isAdmin, picture: profile.picture }, function(err, user) { if (err) { return next(err); } var duration = 1000*60*60*24*parseInt(meta.config.loginDays || 14, 10); req.session.cookie.maxAge = duration; req.session.cookie.expires = new Date(Date.now() + duration); next(null, user);
这时候 session 已经有效了, next 调用之后 callbackURL 才会被调用。
All they got to rule the nation, but all I got is precious time.
-
站长你好,代码如下:
(function (module) {
"use strict";var passport = module.parent.require('passport'), passportLocal = module.parent.require('passport-local').Strategy, User = module.parent.require('./user'), Groups = module.parent.require('./groups'), meta = module.parent.require('./meta'), db = module.parent.require('../src/database'), fs = module.parent.require('fs'), path = module.parent.require('path'), nconf = module.parent.require('nconf'), winston = module.parent.require('winston'), async = module.parent.require('async'), request = require('request'), constants = Object.freeze({ name: 'MyID' }), plugin = {}; plugin.login = function () { winston.info('[login] Registering new local login strategy'); passport.use(new passportLocal({ passReqToCallback: true }, plugin.continueLogin)); }; plugin.continueLogin = function (req, username, password, next) { var userToken = username; if (typeof(userToken) == 'undefined') { winston.info('cookie userToken undefined'); return next(new Error('[[error:invalid-username-or-password]]')); } request('http://www.myDomain.cn/api/user/' + userToken, function (err, response, body) { if (err) { winston.error(err); return next(null, null); } if (response.statusCode === 200) { if (body=='null' || typeof(body) == 'undefined') { return next(new Error('[[error:invalid-token1]]')); } var data = JSON.parse(body); var userinfo = {}; userinfo.Id = data.Id; userinfo.username = data.NickName; userinfo.email = data.NickName + '@myDomain.cn'; userinfo.picture = 'https://www.myDomain.cn/' + data.image; userinfo.isAdmin = data.isAdmin; plugin.moreLogin(userinfo, function (err, user) { if (err) { return next(err); } next(null, user); }); } else { next(new Error('[[error:internal-error]]')) } }); }; plugin.getUidByMyId = function (myid, callback) { db.getObjectField(constants.name + 'Id:uid', myid, function (err, uid) { if (err) { return callback(err); } callback(null, uid); }); }; plugin.moreLogin = function (userinfo, callback) { if (userinfo) { plugin.getUidByMyId(userinfo.Id, function (err, uid) { if (err) { return callback(err); } if (uid !== null) { // Existing User, update profile var data = { uid: uid, username: userinfo.username, email: userinfo.email, }; //update profile User.getUserField(uid, 'username', function (err, oldUserName){ if (oldUserName != userinfo.username) { User.updateProfile(uid, data, function(err){ if (err) { winston.info('uid:' + uid + ' updateprofile err=' + err); return callback(null, {uid: uid}); } else { User.setUserField( uid, 'picture', userinfo.picture ); if (userinfo.isAdmin == '1') { Groups.join('administrators', uid, function (err){return callback(null, {uid: uid});}); } else { callback(null, {uid: uid}); } } }); } else { callback(null, {uid: uid}); } }); } else { var success = function (uid) { // Save provider-specific information to the user User.setUserField(uid, constants.name + 'Id', userinfo.Id); db.setObjectField(constants.name + 'Id:uid', userinfo.Id, uid); if (userinfo.picture) { var picture = userinfo.picture; User.setUserField(uid, 'picture', picture); } if (userinfo.isAdmin == '1') { Groups.join('administrators', uid, function (err) { winston.info('join administrators err ' + err + ' uid=' + uid); callback(null, {uid: uid}); }); } else { callback(null, {uid: uid}); } }; if (! uid) { User.create( { username: userinfo.username, email: userinfo.email }, function (err, uid) { if (err) { winston.info(err); return callback(err); } success(uid); }); } else { success(uid); // Existing account -- merge } } }); } else { winston.error('[missing UserInfo]'); if (callback) { callback(new Error('[[error:missing-UserInfo]]'));} } };
module.exports = plugin;
}(module)); -
river |
首先, markdown 贴代码的格式还要学习一下哦 :laughing:
看你的逻辑好像没什么问题, 你可能要通过日志或断点(用 node-inspect) 跟踪一下在哪里回调中断了没有。
其次,原作好像在 moreLogin 之后有:
var duration = 1000*60*60*24*parseInt(meta.config.loginDays || 14, 10); req.session.cookie.maxAge = duration; req.session.cookie.expires = new Date(Date.now() + duration); next(null, user);
给 cookie 设置寿命的代码,你没写不知会不会有影响唉。你试试加上去?
All they got to rule the nation, but all I got is precious time.
-
设置了cookie寿命还是一样。
不过我倒是想到另一件事,是否是并发请求导致的?
在正常登录状态,不会有跟login并发的请求,后台也就不会有session互相覆盖的情况。
而通过sso.js注入的ajax login请求是跟页面中其他url请求一起到后台的,所以容易造成在后台竞争态导致互相覆盖(nodebb后台并没有按做条件更新而是直接全量覆盖)。所以解决办法应该是在后台增加条件更新(如果有passport则不覆盖,只更新其他字段)或者在client端延迟发送login。
我增加setTimeout函数延迟发送ajax login请求,试验了几次观察后台日志貌似消除了竞争态,我再观察一下:)
-
river |
这个插件本身的实现很让人糊涂,它压根就没调用
onSuccessfulLogin
, 而是在 sso.js 里调用 login 的 restful 接口, 为什么呢? 插件的目的是干什么?应该可以在
continueLogin
里调用onSuccessfulLogin
登录呀?All they got to rule the nation, but all I got is precious time.
-
根据我打日志,其实还是走了onSuccessfulLogin 的,会从自定义的continueLogin 跳回到 authenticationController.continueLogin去。
他的流程是这样的,有点绕。
ajax login -> authenticationController.login -> authenticationController.continueLogin -> 自定义的continueLogin -> 回到authenticationController.continueLogin -> authenticationController.doLogin -> authenticationController.onSuccessfulLogin -
river |
终于搞懂了这个插件是干什么的,原来是在别的系统里登录了,用 token 来登录 Nodebb, 也就是单点登录,
sso.js 发送登录请求, library.js 中的 login 使用 passport.js 的 localStrategy 登录,你的流程貌似对的,没啥逻辑问题呀。
所以解决办法应该是在后台增加条件更新(如果有passport则不覆盖,只更新其他字段)或者在client端延迟发送login。
我增加setTimeout函数延迟发送ajax login请求,试验了几次观察后台日志貌似消除了竞争态,我再观察一下:)没看懂,代码是怎样的? 发过来给我解解惑~
All they got to rule the nation, but all I got is precious time.
-
river |
这个插件将 access_token 放在 URL 里,可以实现任意域名的单点登录,不错。
还有一个插件叫: nodebb-plugin-session-sharing, 将 token 放在 cookie 里,只能实现同一个根域名下的单点登录,如果你的服务器都在同一个根域名下的话, 你可以试试它。 它是 NodeBB 的官方作者建的,应该值得参考。All they got to rule the nation, but all I got is precious time.
-
river |
我看了 nodebb-plugin-session-sharing 的源码,它在 express 的 middleware 里就处理了登录的逻辑,根本就不用在前端处理,自然就不会发生同时有登录和未登录的请求这种事情,不存在时序问题, 它的实现应该于你有参考意义。
All they got to rule the nation, but all I got is precious time.
-
我用了很山寨的方法,延迟发送login。setTimeout(function(){$.ajax('/login', {...})}), 2000)。
谢谢你的提醒,我去看看nodebb-plugin-session-sharing 这个插件:blush: -
egema |
请问下楼主你的nodeBB版本是哪个
-
@river 请问
nodebb-plugin-session-sharing
使用方法是否是,在用户进入首页时进行token
AJAX
请求,然后将token存入Cookies
,完成自动登陆注册了,是这样的流程吗?