在Sails 1.0后端利用OAuth返回的JWT构建用户会话的最佳实践
针对你的SPA认证方案与Sails 1.0适配问题的解答
嘿,我来帮你一步步理清这个问题:
一、Office-JS-Helpers获取的JWT直接用于后端会话是否可行?
这个方案完全可行,但需要注意几个关键前提:
- 必须严格验证JWT的合法性:要确认JWT的签名是微软官方公钥签发的,同时检查
iss(签发者)、aud(受众)、exp(过期时间)这些Claims是否符合你的应用配置,避免伪造的JWT。 - 如果你的后端需要自定义用户权限、关联内部用户数据,或者想自主控制Token的有效期,那生成自定义JWT会更灵活:流程是后端验证Office的JWT合法后,根据JWT中的用户信息(比如邮箱、租户ID)查询你的数据库,生成包含自定义Claims的JWT返回给前端,后续前端用这个自定义JWT和后端交互。
两种方案各有优劣:直接用Office JWT减少了后端的Token生成逻辑,但依赖微软的Token有效期;自定义JWT则让你对认证流程有完全的控制权,更适合复杂的业务场景。
二、Sails 1.0 + Passport.js的适配实现步骤
Sails 1.0的配置结构确实和0.11差异很大,下面是适配1.0版本的具体实践:
1. 安装依赖
首先安装所需的包:
npm install passport passport-azure-ad jsonwebtoken @sailshq/connect-redis
passport-azure-ad:用于验证微软Azure AD签发的JWT(对应Office 365认证)jsonwebtoken:如果要生成自定义JWT的话需要@sailshq/connect-redis:可选,用于生产环境的会话存储(避免默认内存存储的问题)
2. 创建Passport配置文件
在config/目录下新建passport.js,配置Azure AD的Bearer策略:
const passport = require('passport'); const BearerStrategy = require('passport-azure-ad').BearerStrategy; module.exports = function (sails) { // 配置Azure AD策略参数,这些值从你的Azure应用注册中获取 const azureADOptions = { identityMetadata: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}/v2.0/.well-known/openid-configuration`, clientID: process.env.AZURE_CLIENT_ID, validateIssuer: true, issuer: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}/v2.0`, audience: process.env.AZURE_CLIENT_ID, // 必须和你的应用注册的clientID一致 loggingLevel: 'warn', passReqToCallback: false }; // 验证成功后的回调,获取用户信息 const verifyCallback = (token, done) => { // 这里可以根据token中的用户信息(比如token.upn、token.oid)查询你的数据库 // 如果用户不存在,可以自动创建;如果存在,返回用户数据 const user = { id: token.oid, email: token.upn, displayName: token.name }; return done(null, user); }; // 初始化策略 passport.use(new BearerStrategy(azureADOptions, verifyCallback)); return { passport: passport }; };
3. 创建Passport钩子加载中间件
Sails 1.0需要通过钩子来加载Passport中间件,在api/hooks/passport/目录下新建index.js:
module.exports = function (sails) { return { initialize: async function () { const passport = sails.config.passport.passport; // 挂载Passport中间件 sails.app.use(passport.initialize()); // 如果需要基于会话的认证,启用session中间件 sails.app.use(passport.session()); // 序列化用户到会话(如果用会话的话) passport.serializeUser((user, done) => { done(null, user.id); }); // 从会话反序列化用户 passport.deserializeUser((id, done) => { // 这里根据ID查询数据库获取用户信息 User.findOne({ id }).exec((err, user) => { done(err, user); }); }); } }; };
4. 编写认证控制器
在api/controllers/AuthController.js中创建验证接口,接收前端传来的Office JWT:
const passport = require('passport'); const jwt = require('jsonwebtoken'); module.exports = { // 验证Office JWT并处理会话/生成自定义JWT verifyOfficeJwt: function (req, res) { // 使用passport的策略验证JWT passport.authenticate('oauth-bearer', { session: false }, (err, user, info) => { if (err || !user) { return res.unauthorized({ message: 'Invalid or expired Office JWT', error: info }); } // 方案1:直接创建Sails会话 req.session.user = user; return res.ok({ message: 'Session created successfully', user }); // 方案2:生成自定义JWT返回给前端 // const customJwt = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: '24h' }); // return res.ok({ message: 'Custom JWT generated', token: customJwt, user }); })(req, res); } };
5. 配置路由
在config/routes.js中添加认证接口的路由:
module.exports.routes = { // ...其他路由 'POST /api/auth/verify-office-jwt': 'AuthController.verifyOfficeJwt' };
6. 环境变量配置
在config/env/production.js和config/env/development.js中添加你的Azure应用信息和JWT密钥:
module.exports = { // ...其他配置 custom: { AZURE_TENANT_ID: '你的租户ID', AZURE_CLIENT_ID: '你的应用clientID', JWT_SECRET: '强随机字符串,用于签名自定义JWT' } };
最佳实践总结
- 永远用HTTPS传输JWT,避免明文泄露
- 生产环境不要用Sails默认的内存会话存储,配置Redis或数据库存储
- 验证Office JWT时一定要检查所有关键Claims,不要跳过签名验证
- 如果用自定义JWT,设置合理的过期时间,并且定期轮换签名密钥
- 前端拿到JWT后,建议存在HttpOnly的Cookie中(如果用会话),或者localStorage中(无状态模式),但要注意XSS防护
内容的提问来源于stack exchange,提问作者seebiscuit
相关产品推荐
相关产品推荐

