Implementation of authentication and authorization schemes
Basic Concepts of Authentication and Authorization
Authentication is the process of verifying a user's identity, while authorization determines whether a user has permission to access specific resources. In Express applications, these two concepts are often used together to protect routes and API endpoints. Common authentication methods include session-based authentication, JWT (JSON Web Token), and OAuth, while authorization is typically implemented through role or permission systems.
Implementation of Session-Based Authentication
In Express, you can use the express-session
middleware to implement session-based authentication. First, install the dependency:
npm install express-session
Then configure the middleware:
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: { secure: false } // In production, set to true and enable HTTPS
}));
When a user logs in, you can set the session like this:
app.post('/login', (req, res) => {
const { username, password } = req.body;
// Validate user credentials (in a real project, use a database)
if (username === 'admin' && password === 'password') {
req.session.user = { username, role: 'admin' };
return res.send('Login successful');
}
res.status(401).send('Authentication failed');
});
Example of middleware to protect routes:
function requireAuth(req, res, next) {
if (req.session.user) {
return next();
}
res.status(401).send('Login required');
}
app.get('/protected', requireAuth, (req, res) => {
res.send('This is protected content');
});
Implementation of JWT Authentication
JWT is a stateless authentication mechanism suitable for RESTful APIs. First, install jsonwebtoken
:
npm install jsonwebtoken
Example of generating and verifying tokens:
const jwt = require('jsonwebtoken');
const secret = 'your-jwt-secret';
// Login route to generate a token
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
if (username === 'user' && password === 'pass') {
const token = jwt.sign(
{ username, role: 'user' },
secret,
{ expiresIn: '1h' }
);
return res.json({ token });
}
res.status(401).json({ error: 'Authentication failed' });
});
// Middleware to verify the token
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, secret, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
// Protected route
app.get('/api/protected', authenticateToken, (req, res) => {
res.json({ message: `Welcome, ${req.user.username}` });
});
Role-Based Authorization Control
On top of authentication, you can add role checks for authorization:
// Role-check middleware
function requireRole(role) {
return (req, res, next) => {
if (req.user?.role !== role) {
return res.status(403).send('Access denied');
}
next();
};
}
// Admin-only route
app.get('/admin/dashboard',
authenticateToken,
requireRole('admin'),
(req, res) => {
res.send('Admin dashboard');
}
);
OAuth2.0 Integration
Express can integrate Passport.js for OAuth authentication. Example with GitHub OAuth:
npm install passport passport-github2
Configuration example:
const passport = require('passport');
const GitHubStrategy = require('passport-github2').Strategy;
passport.use(new GitHubStrategy({
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
callbackURL: 'http://localhost:3000/auth/github/callback'
},
(accessToken, refreshToken, profile, done) => {
// Find or create the user here
return done(null, profile);
}
));
// Serialize and deserialize the user
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
// Initialize Passport
app.use(passport.initialize());
app.use(passport.session());
// Authentication routes
app.get('/auth/github', passport.authenticate('github'));
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/login' }),
(req, res) => res.redirect('/')
);
Permission Management System
More complex systems may require Permission-Based Access Control (PBAC):
// Permission-check middleware
function checkPermission(permission) {
return (req, res, next) => {
const userPermissions = getUserPermissions(req.user.id); // Hypothetical function
if (!userPermissions.includes(permission)) {
return res.status(403).send('Unauthorized to perform this action');
}
next();
};
}
// Usage example
app.delete('/api/posts/:id',
authenticateToken,
checkPermission('delete_post'),
(req, res) => {
// Logic to delete the post
}
);
Security Best Practices
- Always use HTTPS for sensitive data transmission.
- Set secure cookie attributes:
app.use(session({
// ...other configurations
cookie: {
secure: true,
httpOnly: true,
sameSite: 'strict',
maxAge: 24 * 60 * 60 * 1000 // 1 day
}
}));
- Store passwords using libraries like bcrypt for hashing:
const bcrypt = require('bcrypt');
const saltRounds = 10;
// Hash password during registration
app.post('/register', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// Store username and hashedPassword in the database
});
- Implement rate limiting to prevent brute-force attacks:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5 // Max 5 requests per IP
});
app.use('/login', limiter);
Error Handling
Dedicated middleware for authentication errors:
function authErrorHandler(err, req, res, next) {
if (err.name === 'UnauthorizedError') {
return res.status(401).json({ error: 'Invalid token' });
}
if (err.name === 'ForbiddenError') {
return res.status(403).json({ error: 'Access denied' });
}
next(err);
}
app.use(authErrorHandler);
Testing Authentication and Authorization
Use Supertest to test endpoints:
const request = require('supertest');
const app = require('../app');
describe('Authentication Tests', () => {
it('Unauthenticated user accessing protected route should return 401', async () => {
const res = await request(app).get('/protected');
expect(res.statusCode).toEqual(401);
});
it('Valid token should grant access', async () => {
const loginRes = await request(app)
.post('/login')
.send({ username: 'user', password: 'pass' });
const token = loginRes.body.token;
const res = await request(app)
.get('/protected')
.set('Authorization', `Bearer ${token}`);
expect(res.statusCode).toEqual(200);
});
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:数据验证与输入过滤
下一篇:日志系统的集成与配置