.
分隔的三部分在header中通常包含了两部分:token类型和采用的加密算法。
{ "alg": "HS256", "typ": "JWT"}
接下来对这部分内容使用Base64Url
编码组成了JWT
结构的第一部分。
负载就是存放有效信息的地方。这个名字像是指货车上承载的货物,这些有效信息包含三个部分
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息
{ "sub": "1234567890", "name": "zfpx", "admin": true}
上述的负载需要经过Base64Url
编码后作为JWT结构的第二部分
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Authorization: Bearer <token>
module.exports = {
dbUrl: 'mongodb://127.0.0.1/jwt',
secret: 'zfpx'
}
const express = require('express');
const jwt = require('jwt-simple');
const bodyParser = require('body-parser');
const moment = require('moment');
const User = require('./model/user');
const jwtWare = require('./jwt');
const { secret } = require('./config');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.post('/signup', async function (req, res) {
let user = req.body;
user = await User.create(user);
if (user) {
res.json({
code: 0,
data: {
user
}
});
} else {
res.json({
code: 1,
data: '用户注册失败'
});
}
});
app.post('/login', async function (req, res) {
let user = req.body;
user = await User.findOne(user);
if (user) {
let expires = moment().add(7, 'days').valueOf();
let userInfo = {
id: user._id,
username: user.username
};
let token = jwt.encode({
user: userInfo,
exp: expires
}, secret);
res.json({
code: 0,
data: {
token,
expires,
user: userInfo
}
});
} else {
res.json({
code: 1,
data: '用户名或密码错误'
});
}
});
app.get('/user', jwtWare, function (req, res) {
res.json({
code: 0,
data: {
user: req.user
}
});
});
app.listen(8080);
const { secret } = require('./config');
const jwt = require('jwt-simple');
const User = require('./model/user');
module.exports = async function (req, res, next) {
let authorization = req.headers['authorization'];
if (authorization) {
try {
let decoded = jwt.decode(authorization.split(' ')[1], secret);
req.user = decoded.user;
next();
} catch (err) {
console.log(err);
res.status(401).send('Not Allowed');
}
} else {
res.status(401).send('Not Allowed');
}
}
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let ObjectId = Schema.Types.ObjectId;
let { dbUrl } = require('../config');
let conn = mongoose.createConnection(dbUrl);
let UserSchema = new Schema({
username: String,
password: String
});
module.exports = conn.model("User", UserSchema);
create-react-app front
cd front
cnpm i react react-dom react-router-dom axios -S
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Switch, Redirect } from 'react-router-dom';
import createHashHistory from 'history/createHashHistory';
import Login from './components/Login';
import User from './components/User';
const history = createHashHistory();
ReactDOM.render(
<Router history={history}>
<Switch>
<Route exact path="/" component={Login} />
<Route path="/user" component={User} />
<Redirect to="/" />
</Switch>
</Router>, document.querySelector('#root')
);
import axios from 'axios';
import createHashHistory from 'history/createHashHistory';
const history = createHashHistory();
axios.interceptors.request.use(config => {
if (localStorage.token) {
config.headers.Authorization = 'Bearer ' + localStorage.token
}
return config
}, error => {
return Promise.reject(error)
})
axios.interceptors.response.use(res => {
if (res.data.code != 0) {
return Promise.reject(res);
}
return res;
}, error => {
if (error.response.status == 401) {
history.push('/');
}
return Promise.reject(error.response.data);
});
export function login(data) {
return axios({
url: 'http://localhost:8080/login',
method: 'post',
data
}).then(response => {
let data = response.data;
localStorage.setItem('token', data.data.token);
return data;
})
}
export function getUser(data) {
return axios({
url: 'http://localhost:8080/user',
method: 'get'
}).then(response => {
return response.data;
})
}
import React, { Component } from 'react';
import { login } from '../api';
export default class Login extends Component {
handleSubmit = (event) => {
event.preventDefault();
let username = this.username.value;
let password = this.password.value;
login({ username, password }).then(data => {
if (data.code == 0) {
this.props.history.push('/user');
}
});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名<input required ref={ref => this.username = ref} />
密码<input required ref={ref => this.password = ref} />
<input type="submit" />
</form>
)
}
}
import React, { Component } from 'react';
import { getUser } from '../api';
export default class User extends Component {
state = {
user: {}
}
componentDidMount() {
getUser().then(res => {
if (res && res.code == 0) {
this.setState({ user: res.data.user });
}
});
}
render() {
return (
<div>
欢迎 {this.state.user.username}
</div>
)
}
}
jwt.js
const crypto = require('crypto');
function encode(payload, key) {
let header = { type: 'JWT', alg: 'sha256' };//声明类型和算法
var segments = [];//声明一个数组
segments.push(base64urlEncode(JSON.stringify(header)));//对header进行base64
segments.push(base64urlEncode(JSON.stringify(payload)));//对负载进行base64
segments.push(sign(segments.join('.'), key));//加入签名
return segments.join('.');
}
function sign(input, key) {
return crypto.createHmac('sha256', key).update(input).digest('base64');
}
function decode(token, key) {
var segments = token.split('.');
var headerSeg = segments[0];
var payloadSeg = segments[1];
var signatureSeg = segments[2];
var header = JSON.parse(base64urlDecode(headerSeg));
var payload = JSON.parse(base64urlDecode(payloadSeg));
if (signatureSeg != sign([headerSeg, payloadSeg].join('.'), key)) {
throw new Error('verify failed');
}
if (payload.exp && Date.now() > payload.exp * 1000) {
throw new Error('Token expired');
}
return payload;
}
function base64urlEncode(str) {
return new Buffer(str).toString('base64');
}
function base64urlDecode(str) {
return new Buffer(str, 'base64').toString();
}
module.exports = {
encode,
decode
}