在Node项目中使用Passport.js实现JWT

720 阅读6分钟

什么是JWT?

JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。该信息可以验证和信任,因为它是经过数字签名的。JWT通常用于身份验证和授权,因为它可以轻松地传递用户身份和角色信息。

JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部包含JWT使用的算法和令牌类型。载荷包含实际的数据,如用户ID、角色等信息。签名使用头部和载荷以及密钥的组合生成,用于验证令牌是否被篡改。

JWT的优点包括:

  • 简单:JWT是一种紧凑的、自包含的方式,易于使用和传输。
  • 安全:JWT使用数字签名验证令牌是否被篡改。
  • 通用:JWT标准被广泛支持,并可用于多种编程语言和框架中。

JWT的工作原理

JWT的工作原理可以简述如下:

  • 用户使用用户名和密码进行身份验证。
  • 服务器对用户进行身份验证,并生成JWT。
  • JWT被返回给用户,以便将其存储在客户端中。
  • 用户将JWT用于后续请求的身份验证和授权。

在JWT中,载荷包含用户信息,例如用户ID和角色,这些信息可以使用标准化的声明,例如“sub”(主题)、“exp”(到期时间)和“iss”(发行人)进行组织。这些声明以键值对的形式出现,并以Base64编码的字符串进行编码。

JWT的签名是使用头部和载荷以及密钥的组合生成的。这个过程可以使用各种加密算法完成,例如HMAC、RSA和ECDSA。签名的目的是确保令牌未被篡改,因此服务器可以信任令牌的内容。

使用Koa2和Passport.js实现JWT

现在,我们将使用Koa2和Passport.js来实现JWT。

1. 创建Koa2应用程序

我们首先需要创建一个Koa2应用程序。在您的终端中,使用以下命令创建一个名为“koa-jwt”的新目录:

mkdir koa-jwt
cd koa-jwt

然后,使用以下命令初始化新的npm项目:

npm init -y

接下来,使用以下命令安装必要的依赖项:

npm i koa koa-router koa-bodyparser koa-passport passport-jwt jsonwebtoken

在安装完依赖项之后,我们可以开始配置Koa2应用程序并实现JWT。

2. 配置Passport.js

我们需要使用Passport.js来验证JWT并保护我们的API路由。我们将使用Passport.js的JWT策略来验证JWT。

在项目文件夹中创建一个名为config的文件夹,并在其中创建一个名为passport.js的文件:

const passportJWT = require('passport-jwt');
const ExtractJwt = passportJWT.ExtractJwt;
const JwtStrategy = passportJWT.Strategy;

const jwtOptions = {
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
    secretOrKey: 'your_jwt_secret'
};

module.exports = passport => {
    passport.use(
        new JwtStrategy(jwtOptions, (jwtPayload, done) => {
            // 检查数据库中是否存在用户(为了方便,写死123)
            if (jwtPayload.id === 123) {
                done(null, { id: 123,role: 'admin'})
            } else {
                done(null, false);
            }
        })
    );
};

在这个文件中,我们首先导入了passport-jwtjsonwebtoken模块。然后,我们定义了jwtOptions对象,它包含从请求头中提取JWT的选项和JWT的密钥。我们使用ExtractJwt.fromAuthHeaderAsBearerToken()方法从Bearer Token中提取JWT。

接下来,我们使用Passport.js的JwtStrategy构造函数创建一个新的JWT策略。在策略的回调函数中,我们首先检查JWT有效载荷中的用户ID是否存在于数据库中。如果用户存在,则调用done(null, user)函数。如果用户不存在,则调用done(null, false)函数。

最后,我们使用module.exports导出了一个函数,该函数将Passport.js作为参数并在其中定义了我们的JWT策略。

3. 配置Koa2服务器和中间件

接下来,我们需要配置Koa2服务器和中间件。在项目文件夹中创建一个名为app.js的文件,并将以下代码添加到文件中:

const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const passport = require('koa-passport');
const jwtConfig = require('./config/passport');
const authRoutes = require('./routes/auth');

const app = new Koa();

app.use(bodyParser());
app.use(passport.initialize());
jwtConfig(passport);
app.use(authRoutes.routes());

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

在这个文件中,我们首先导入了Koa、Koa BodyParser、Passport.js和authRoutes路由。然后,我们创建了一个新的Koa实例,并使用app.use中间件将Body Parser、Passport.js和我们的路由绑定到应用程序中。在jwtConfig函数中,我们将Passport.js作为参数,并配置了JWT策略。

最后,我们使用app.listen方法启动服务器并在端口3000上侦听连接。

4. 创建授权路由

现在我们需要创建一个授权路由,该路由将使用JWT保护我们的API路由。在项目文件夹中创建一个名为routes的文件夹并在其中创建一个名为auth.js的文件:

const Router = require('koa-router');
const jwt = require('jsonwebtoken');
const passport = require('koa-passport');

const router = new Router();

router.post('/login', async (ctx, next) => {
    const { username, password } = ctx.request.body;

    // 检查用户名和密码是否正确(这里为了方便,写死了username和password)
    if (username === 'admin' && password === 'password') {
        const payload = { id:123 };
        const token = jwt.sign(payload, 'your_jwt_secret');
        ctx.body = { token };
    } else {
        ctx.status = 401;
        ctx.body = { error: '用户名或密码无效!' };
    }
});

router.get('/protected', passport.authenticate('jwt', { session: false }), async (ctx, next) => {
    const user = ctx.state.user;
    // 检查用户是否存在
    if (!user) {
        ctx.status = 401;
        ctx.body = { error: '无效token!' };
        return;
    }

    // 检查用户是否有权访问资源
    if (user.role !== 'admin') {
        ctx.status = 403;
        ctx.body = { error: '访问被拒绝!' };
        return;
    }

    ctx.body = { message: '通过!' };
});

module.exports = router;

在这个文件中,我们首先导入了Koa Router、jsonwebtoken和Passport.js。然后,我们创建了一个新的路由,并定义了两个路由处理程序:/login/protected

/login用于验证用户名和密码,并生成JWT。如果用户名和密码正确,则我们使用jwt.sign方法生成JWT,并将其返回给客户端。否则,我们返回401错误和错误消息。

/protected中,我们首先使用ctx.state.user来获取JWT策略中传递的用户对象。然后,我们检查用户对象是否存在。如果不存在,则返回401错误和错误消息。这表示JWT验证不通过。

接下来,我们检查用户对象中的角色属性。如果用户角色不是“admin”,则返回403错误和错误消息。这表示用户不允许访问受保护的资源。

最后,如果JWT验证通过并且用户被授权访问受保护的资源,则返回受保护的资源。

5. 测试应用程序

现在,我们已经创建了一个Koa2应用程序,并使用Passport.js实现了JWT验证。让我们使用Postman测试我们的应用程序。

首先,我们需要使用以下JSON数据向/login路由发出POST请求:

{
  "username": "admin",
  "password": "password"
}

如果用户名和密码正确,则服务器将返回一个JWT,例如:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTIzLCJpYXQiOjE2MTc1NjE2MDZ9.CjajG6SXrYJ8gU6_JK_OE92nH9Fk-6ScDjszgCkxKJw"
}

现在,我们可以使用此JWT向/protected路由发出GET请求,并使用Bearer Token将JWT传递给服务器。如果JWT验证通过并且用户被授权访问受保护的资源,则服务器将返回:

{ 
    message: '通过!'
}

如果JWT验证不通过,则服务器将返回:

{ 
    error: '无效token!'
}

如果JWT验证通过但用户没有被授权访问受保护的资源,则服务器将返回:

{ 
    error: '访问被拒绝!'
}

结论

本文详细介绍了JWT的工作原理以及如何使用Koa2和Passport.js实现JWT身份验证。我们通过创建Passport.js JWT策略和授权路由,保护了我们的API路由。如果您需要身份验证和授权解决方案,JWT和Passport.js是非常好的选择。