Sails JS 2018用户管理最佳实践:多权限与子域名配置问询
Hey there! Since you're working with Sails.js v0.12.13 and need multi-tenant subdomain support plus layered role-based access control without Passport, here's a practical, battle-tested approach tailored to your setup:
Best Practices for Multi-Tenant & Role-Based Access in Sails.js v0.12.13
1. Multi-Tenant Subdomain Handling
First, you’ll need to map subdomains like customer1.app.com to individual customer records. Here’s how to implement this seamlessly:
- Subdomain Parsing Middleware: Add custom middleware in
config/http.jsto extract the subdomain and attach the corresponding customer to the request:// config/http.js module.exports.http = { middleware: { order: [ 'startRequestTimer', 'parseSubdomain', // Insert this before other auth-related middleware 'cookieParser', // ... rest of your middleware order ], parseSubdomain: function(req, res, next) { const host = req.hostname; const parts = host.split('.'); // Adjust this logic to match your base domain structure if (parts.length >= 3 && parts.slice(-2).join('.') === 'app.com') { req.subdomain = parts[0]; // Fetch customer from DB using the subdomain Customer.findOne({ subdomain: req.subdomain }).exec((err, customer) => { if (err) return next(err); if (!customer) return res.notFound('Customer not found'); req.customer = customer; next(); }); } else { // Root domain (e.g., app.com) for global admin access req.subdomain = null; next(); } } } }; - Customer Model: Create a
Customermodel with a uniquesubdomainfield to link subdomains to customer data:// api/models/Customer.js module.exports = { attributes: { subdomain: { type: 'string', required: true, unique: true }, name: { type: 'string', required: true }, // Add other customer-specific fields as needed } }; - Hosting Setup: Ensure your DNS has a wildcard record (
*.app.com) pointing to your server. If using a reverse proxy like Nginx, configure it to pass the full host header to Sails.
2. Role-Based Access Control (RBAC) with Policies
Sails’ built-in policies are perfect for enforcing granular permissions. Here’s how to structure them:
- Enhance User Model: Update your
Usermodel to include role and customer association:// api/models/User.js module.exports = { attributes: { email: { type: 'string', required: true, unique: true }, password: { type: 'string', required: true }, role: { type: 'string', enum: ['global-admin', 'global-user', 'customer-admin', 'customer-user'], required: true }, customer: { model: 'Customer', allowNull: true } // Null for global roles } }; - Create Core Policies: Build reusable policies in
api/policies/to check authentication and roles:isAuthenticated.js: Verify the user is logged in via sessionmodule.exports = function(req, res, next) { if (req.session.userId) { User.findOne(req.session.userId).exec((err, user) => { if (err) return next(err); if (!user) return res.forbidden('Please log in to access this resource'); req.user = user; next(); }); } else { return res.forbidden('Please log in to access this resource'); } };isGlobalAdmin.js: Restrict access to global admins onlymodule.exports = function(req, res, next) { if (req.user.role === 'global-admin') return next(); return res.forbidden('Only global admins can access this resource'); };isCustomerAdmin.js: Ensure the user is the admin of the current subdomain’s customermodule.exports = function(req, res, next) { if (!req.customer) return res.forbidden('This resource requires a customer context'); if (req.user.role === 'customer-admin' && req.user.customer === req.customer.id) { return next(); } return res.forbidden('Only this customer\'s admin can access this resource'); };
- Apply Policies to Routes: Assign policies in
config/routes.jsto protect your endpoints:// config/routes.js module.exports.routes = { // Global admin routes 'GET /admin/dashboard': { controller: 'AdminController', action: 'dashboard', policies: ['isAuthenticated', 'isGlobalAdmin'] }, // Customer admin routes 'GET /customer/settings': { controller: 'CustomerController', action: 'settings', policies: ['isAuthenticated', 'isCustomerAdmin'] }, // Customer user routes (create a combined policy for users/admins if needed) 'GET /customer/dashboard': { controller: 'CustomerController', action: 'dashboard', policies: ['isAuthenticated', 'isCustomerUserOrAdmin'] }, };
3. Custom Authentication (No Passport)
Since you don’t need social login, a simple session-based auth system works perfectly:
- Password Hashing: Use
bcryptto hash passwords before saving users. Add a lifecycle hook to theUsermodel:
Install the compatible bcrypt version:// api/models/User.js const bcrypt = require('bcrypt'); module.exports = { attributes: { /* ... */ }, beforeCreate: function(values, next) { // Use bcrypt v0.8.7 (compatible with Sails 0.12's Node.js version) bcrypt.hash(values.password, 10, (err, hash) => { if (err) return next(err); values.password = hash; next(); }); } };npm install bcrypt@^0.8.7 - Login/Logout Actions: Create an
AuthControllerto handle user authentication:// api/controllers/AuthController.js const bcrypt = require('bcrypt'); module.exports = { login: function(req, res) { const { email, password } = req.body; User.findOne({ email }).exec((err, user) => { if (err) return res.serverError(err); if (!user) return res.badRequest('Invalid email or password'); bcrypt.compare(password, user.password, (err, match) => { if (err) return res.serverError(err); if (!match) return res.badRequest('Invalid email or password'); // Store user ID in session req.session.userId = user.id; return res.ok('Logged in successfully'); }); }); }, logout: function(req, res) { req.session.destroy(); return res.ok('Logged out successfully'); } }; - Session Configuration: For production, switch from the default memory session store to a persistent option like Redis (configure in
config/session.js).
4. Future-Proofing Tips
- Role Abstraction: Instead of hardcoding roles, consider a
Rolemodel with linked permissions. This lets you add new roles later without rewriting policies. - Tenant Isolation: Add lifecycle hooks to customer-specific models to automatically filter queries by
customer: req.customer.id, preventing cross-tenant data leaks. - Testing: Write unit tests for your policies and authentication flows to ensure permissions work as expected when adding new roles or features.
内容的提问来源于stack exchange,提问作者StS
相关产品推荐
相关产品推荐

