Express, Passport and JSON Web Token(jwt) 认证入门

在使用前端框架的时候,我们的后端平台可能是 API,我们在每次请求的时候发送 jwt 而不是依赖于 Session。我们完成两个路由功能

  1. 创建 /login 路由请求令牌
  2. 创建 /secret 路由,只有持有令牌的认证用户能够访问。

首先, 创建项目目录。

1
2
3
4
mkdir project-dir
cd project-dir
npm init -y
yarn add express body-parser passport passport-jwt jsonwebtoken lodash

我们先来创建 index.js,启动一个最简单的 express

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// file: index.js
const express = require('express')
const app = express()

app.get('/', (req, res) => {
    res.json({message: 'Express is up!'})
})

app.listen(3000, () => {
    console.log('Express running')
})

启动我们的服务器

1
nodemon index.js

访问 http://localhost:3000 查看结果。 /2017/05/express-passport-jwt/express-passport-jwt-01.png

修改我们的 index.js,导入相应的包

1
2
3
4
5
6
7
8
9
const _ = require('lodash')
const express = require('express')
const bodyParser = require('body-parser')
const jwt = require('jsonwebtoken')
const passport = require('passport')
const passportJWT  = require('passport-jwt')

const ExtractJWT = passportJWT.ExtractJWT
const JwtStrategy = passportJWT.Strategy

下面创建模拟用户数组,实际工作中这些数据应该来自数据库,但现在我们采用数组。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let users = [
    {
        id: 1,
        name: 'zhangjinglin',
        password: '111111'
    },
    {
        id: 2,
        name: 'test',
        password: '222222'
    }
]

passport.js 中存在 策略 的概念。他们是一系列的中间件,用于处理请求如何到达路由。如果定义的身份验证策略失败,这意味着回调将被调用,回调中的错误不为 null 或第二个参数表示用户对象但是为 false,那么该路由将不会被调用,而是将发送 401 未授权的响应。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let jwtOptions = {
    jwtFromRequest: ExtractJWT.fromAuthHeader(),
    secretOrKey:  'zhangjinglin'
}

let strategy = new JwtStrategy(jwtOptions, (jwt_payload, next) => {
    console.log('payload received', jwt_payload)
    let user = users[_.findIndex(users, {id: jwt_payload.id})]
    if (user) {
        next(null, user)
    } else {
        next(null, false)
    }
})

passport.use(strategy)

使用 passport body-parser

1
2
3
4
5
6
7

const app = express()

app.use(passport.initialize())

app.use(bodyParser.urlencoded({extended: true}))
app.use(bodyParser.json())

下面我们完成登录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
app.post('/login', (req, res) => {
    if (req.body.name && req.body.password) {
        let name = req.body.name
        let password = req.body.password
        //通常应该访问数据库
        let user = users[_.findIndex(users, { name })]
        if (!user) {
            res.status(401).json({ message: 'no such user found' })
        }
        if (user.password === password) {
            let payload = { id: user.id }
            let token = jwt.sign(payload, jwtOptions.secretOrKey)
            res.json({message: 'ok', token})
        } else {
            res.status(401).json({message: 'password not correct'})
        }
    } else {
        res.status(401).json({message: 'name and password must be vaid'})
    }
}

/2017/05/express-passport-jwt/express-passport-jwt-02.png 下面我们要准备保护我们的路由

1
2
3
app.get('/secret', passport.authenticate('jwt', { session: false }), (req, res) => {
    res.json('Success! You can not see this without a token')
})

直接访问,不提供token /2017/05/express-passport-jwt/express-passport-jwt-03.png

提供token /2017/05/express-passport-jwt/express-passport-jwt-04.png

OK,大功告成!