Passport req.login()是否在Session中持久化用户?SAML认证疑问
SAML认证中req.isAuthenticated()的行为疑问
我用Node.js Express开发已有两年,但Passport/SAML认证完全是新领域。相关文档稀缺、不完整还经常矛盾,官方也没有真实场景的示例,找到的示例都没法直接运行,得自己调整才能用。不过我已经实现了以WordPress作为IDP的概念验证。
现在对req.isAuthenticated()的行为有疑问:登录之后,后续的授权请求会跳过IDP,直接调用passport.deserializeUser()——这个方法只从Session里取上次保存的用户信息,只要不为空就判定已认证。问题是,当我在WordPress登出后,我的网站依然认为用户处于登录状态,直到Session过期。
这是预期行为吗?我注意到如果在ACS里不调用req.logIn(),就会跳过整个passport.serializeUser()和deserializeUser()流程,这样每次请求都会走IDP,符合我的需求,但这时我必须自定义isAuthenticated检查逻辑。我不想大量修改现有代码,希望用原生的解决方案。
请问我是不是遗漏了什么?以下是我的相关代码实现:
AuthorizationController实现
import * as express from 'express'; import { ApiRoot } from '..'; import { passport } from '../Services/Saml'; import bodyParser from 'body-parser'; class AuthorizationController { private _Path = '/Authorization'; private router = express.Router(); /** * Create a new instance of our AuthorizationController object. */ constructor() { // Set the routes and end points. We only need one for now, the one that consumes the SAML. this.router.post( `${ApiRoot}${this._Path}/SAML/Consume`, bodyParser.urlencoded({ extended: false }), this.ConsumeSaml ); } /****************************************************************************************/ /** * Consume the SAML returned by the IDP. */ private ConsumeSaml = (req: any, res: express.Response, next: any) => { passport.authenticate( "saml", { session: false }, (_err, user: any) => { // We need to call req.logIn() (because this is apparently a custom ACS) so that // passport serializeUser() and deserializeUser() gets called. req.logIn(user, (err: any) => { if (err) { return (next(err)); } else { res.redirect(req.body.RelayState); } }); } )(req, res, next); } } export default AuthorizationController;
Passport SAML策略与序列化配置
import passport from "passport"; import SysTrick from "../Helpers/SysTrick"; const SamlStrategy = require('passport-saml').Strategy; // Setup the SAML configuration. const _SamlConfig = { entryPoint: "entrypoint", callbackUrl: `callback`, issuer: "issuer", cert: "cert", }; // Setup the SAML stragety. const _Strategy = new SamlStrategy( _SamlConfig, (profile, done) => { console.log("strategy"); return (done(null, { "id": profile.nameID })); } ); // Setup passport. passport.use('saml', _Strategy); passport.serializeUser((user: any, done) => { console.log(`serializeUser| id = ${user.id}`); done(null, user.id); }); passport.deserializeUser((id, done) => { console.log(`deserializeUser| id = ${id}`); return (done(null, (id ? { "id" : id } : null))); }); export { passport };
服务器初始化代码
this._App.use(bodyParser.urlencoded({ extended: false })) this._App.use(bodyParser.json()) // Session configuration. const sessionConfig: any = { secret: "secret", saveUninitialized: false, cookie: { maxAge: 1000 * 60 * 60 * 2 }, resave: false, store: new this._FileStore({ path: './sessions/', }), } if (SysTrick.IsProduction) { this._App.set('trust proxy', 1); //.. trust first proxy sessionConfig.cookie.secure = true; //.. serve secure cookies (can only be used with https clients) } // Enable cors if we are in development. if (!SysTrick.IsProduction) { this._App.use(cors()); } // Limit the size of the inload that will be processed. this._App.use(express.json({ limit: '15mb' })); // Add security. this._App.use(helmet({ contentSecurityPolicy: false, //.. easiest way to allow external scripts (e.g., for awsomefonts) to run frameguard: false //.. allow views to show in iframes })); // Have Node serve our React application (views). this._App.use(express.static(App.ViewsPath)); // Setup the Express global error handler. this._App.use( function(error: any, _req: express.Request, res, next) { // Log the error. try { Log.Error(error, "Global Error Handler"); } catch { /* */ } // Display the error on the console. console.log(chalk.red.bold("ERROR")); console.log(chalk.red.bold("=====")); console.log(error); // Because we hook post-response processing into the global error handler, we get // to leverage unified logging and error handling; but, it means the response may // have already been committed, since we don't know if the error was thrown PRE or // POST response. As such, we have to check to see if the response has been // committed before we attempt to send anything to the user. if (!res.headersSent) { res.status(500).send("Internal server error."); } } ); // Add the session storage. this._App.use(session(sessionConfig)); // Add passport for SAML SSO. this._App.use(passport.initialize()); this._App.use(passport.session());
路由与认证中间件
// Test public route. this._App.get( "/public", CheckAuthorization, (req, res, next) => { res.send("I have been authorised!"); } ); // This route is for passport-SAML authorization via WordPress. this._App.get( App.LoginUrl, (req: any, res, next) => { // We set the req.query.RelayState, after using session in the Check-Auth() // middlware. The reason why we have to use req.query.RelayState and not session // all the way to the ACS is because the session value is undefined for some reaon // in the ACS. req.query.RelayState = req.session.OriginalUrl; passport.authenticate('saml')(req, res, next); } ); // This middleware is for protecting routes with with passport-SAML authorization. function CheckAuthorization(req: any, res: any, next: any) { // The IDP is bypassed, constantly calling passport.deserializeUser(), which has a valid user // if the session hasn't ended. Will have to override this checker. if (req.isAuthenticated()) { next(); } else { // NOTE: We use session here because saving to req.query.RelayState does not // work from this middleware. It is in the actual route that calls // passport.authenticate() that req.query.RelayState works. This is all an ugly // hack. This whole SAML business is a hack, and proper documentation/example is // non-existent. req.session.OriginalUrl = req.originalUrl; // Redirect to the login route, which is really a call to the IDP. res.redirect(App.LoginUrl); } }
内容的提问来源于stack exchange,提问作者abc
相关产品推荐
相关产品推荐

