AJAX-4 JWT和axios拦截器

AJAX-4 JWT和axios拦截器

JWT介绍

JSON Web Token是目前最为流行的跨域认证解决方案

如何获取:在使用 JWT 身份验证中,当用户使用其凭据成功登录时,将返回 JSON Web Token(令牌)

image-20230430124920511

登录成功之后,服务器会返回 token

作用: 允许用户访问使用该令牌(token)允许的路由、服务和资源

image-20230430124948128

首页-页面访问控制

在访问特定页面的时候,根据是否登录来决定是否允许访问

image-20230430125042982

核心步骤:

  1. 抽取校验函数(多个页面需要使用)
  2. 判断token(缓存中的token)
  3. 提示用户并跳转登录页
  4. 页面调用(目前考虑首页即可)

关键代码:

  1. common.js
1
2
3
4
5
6
7
8
9
10
11
12
13
// 抽取校验函数(判断是否登录)
function checkLogin() {
// 判断token
const token = localStorage.getItem('token')
// console.log(token)
// token为null说明没有缓存
if (token === null) {
showToast('请先登录')
setTimeout(() => {
location.href = 'login.html'
}, 1500)
}
}
  1. index.js
1
2
// 调用判断是否登录的函数
checkLogin()

首页-用户名渲染

渲染缓存中的用户名

需求:

  • 将登录成功之后缓存的用户名,渲染到页面的右上角
image-20230430125320181

核心步骤:

  1. 抽取渲染函数(多页面使用)
  2. 读取并渲染用户名(缓存中)
  3. 页面调用函数(目前考虑首页)

关键代码:

  1. common.js
1
2
3
4
5
6
7
// 抽取渲染函数(渲染缓存中的用户名)
function renderUsername() {
// 读取并渲染用户名
const username = localStorage.getItem('username')
// console.log(username)
document.querySelector('.username').innerText = username
}
  1. index.js
1
2
// 调用渲染用户名的函数
renderUsername()

首页-退出登录

完成首页退出登录操作

需求:

  1. 点击退出按钮,删除缓存数据(token,用户名)
  2. 返回登录页
image-20230430125640254

核心步骤:

  1. 抽取退出登录函数(复用)
  2. 绑定点击事件
  3. 删除缓存并跳转登录页(token,用户名)
  4. 页面调用(目前考虑首页)

关键代码:

  1. common.js
1
2
3
4
5
6
7
8
9
10
11
// 抽取退出登录函数
function registerLogout() {
// 绑定点击事件
document.querySelector('#logout').addEventListener('click', () => {
// console.log('点了退出')
// 删除缓存并跳转登录页
localStorage.removeItem('username')
localStorage.removeItem('token')
location.href = 'login.html'
})
}
  1. index.js
1
2
// 调用退出登录函数 给退出按钮注册点击事件
registerLogout()

首页-统计数据

获取首页统计数据并渲染

需求:

  1. 调用接口获取数据并渲染
image-20230430125914532

数据接口:

  1. 统计数据接口需要登录才可以调用
  2. 调用时需要在请求头中携带token
image-20230430130049903

axios设置请求头:

  1. headers属性设置对象
  2. key:根据文档设置,比如Authorization
  3. value:携带到服务器的值
1
2
3
4
5
6
axios({
  url: '/dashboard',
  headers: {
    Authorization: 'token'
  }
})

核心步骤:

  1. 根据文档调用接口
  2. 渲染数据

关键代码:

  1. index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 首页-统计数据
async function getData() {
const token = localStorage.getItem('token')
// 调用接口(登录成功之后才可以调用)
const res = await axios({
url: '/dashboard',
// 请求头中携带token
// 不携带token,直接报错
headers: {
Authorization: token
}
})
const overview = res.data.data.overview

// 渲染数据
Object.keys(overview).forEach(key => {
document.querySelector(`.${key}`).innerText = overview[key]
})
}

getData()

首页-登录状态失效

首页-登录状态失效

需求:

  1. 调用接口时,token
    1. 有效:正常调用
    2. 无效: 提示用户,清除缓存,返回登录页
image-20230430130524548

核心步骤:

  1. 判断token失效(401状态码)
  2. 删除缓存并提示用户
  3. 返回登录页
  4. **注意:**可以通过修改缓存中的token模拟失效,默认失效时间(2个小时)

关键代码:

  1. index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 首页-统计数据
async function getData() {
const token = localStorage.getItem('token')
try {
// 调用接口(登录成功之后才可以调用)
const res = await axios({
url: '/dashboard',
// 请求头中携带token
// 不携带token,直接报错
headers: {
Authorization: token
}
})
const overview = res.data.data.overview

// 渲染数据
Object.keys(overview).forEach(key => {
document.querySelector(`.${key}`).innerText = overview[key]
})
} catch (error) {
// 首页-登录状态过期
// 判断token失效(状态码401):token过期,token被篡改
if (error.response.status === 401) {
// 删除缓存并提示用户
localStorage.removeItem('username')
localStorage.removeItem('token')
// 使用普通用户可以理解的方式提示他们
showToast('请重新登录')

// 返回登录页
setTimeout(() => {
location.href = 'login.html'
}, 1500)
}
}

}

getData()

axios-拦截器

作用: 请求发送之前,响应回来之后执行一些 公共 的逻辑

image-20230430130855493
  1. 注册之后,调用接口
  2. 请求发送时–》执行请求拦截器–》服务器
  3. 服务器响应内容–》执行响应拦截器–》接收数据

axios请求拦截器-统一设置token

通过请求拦截器统一设置token

需求:

  1. 通过请求拦截器统一设置token
  2. 设置一次之后后续调用接口不用单独设置

请求拦截器-基本写法:

1
2
3
4
5
6
7
8
9
10
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么,比如: 统一设置token
// 通过config可以获取请求的设置,比如headers可以获取(修改)请求头
  return config;
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error);
});

核心步骤:

  1. 添加请求拦截器
  2. 统一设置token
  3. 移除首页对应逻辑

关键代码:

  1. commons.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 添加请求拦截器
// 统一携带token
axios.interceptors.request.use(function (config) {
// 可以通过headers,查看+设置请求头
// config.headers['info'] = 'itheima666'
// 每次发送请求,都会执行这个回调函数
// console.log(config)
// 在发送请求之前做些什么,比如: 统一设置token
const token = localStorage.getItem('token')
// token存在,才携带
if (token) {
config.headers['Authorization'] = token
}
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});

  1. index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 首页-统计数据
async function getData() {
try {
// 调用接口(登录成功之后才可以调用)
const res = await axios({
url: '/dashboard',
// 通过axios请求拦截器统一携带
})
const overview = res.data.data.overview

// 渲染数据
Object.keys(overview).forEach(key => {
document.querySelector(`.${key}`).innerText = overview[key]
})
} catch (error) {
// 首页-登录状态过期
// 判断token失效(状态码401):token过期,token被篡改
// console.dir(error)
if (error.response.status === 401) {
// 删除缓存并提示用户
localStorage.removeItem('username')
localStorage.removeItem('token')
// 使用普通用户可以理解的方式提示他们
showToast('请重新登录')

// 返回登录页
setTimeout(() => {
location.href = 'login.html'
}, 1500)
}
}
}

axios响应拦截器-统一处理token失效

axios响应拦截器-统一处理token失效

需求:

  1. 通过 axios响应拦截器-统一处理token失效
image-20230430131613785

响应拦截器-基本写法:

1
2
3
4
5
6
7
8
9
10
11
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么,比如: 数据剥离
  return response;
}, function (error) {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么: 比如统一处理token失效
  return Promise.reject(error);
});

核心步骤:

  1. 添加响应拦截器
  2. 统一处理token失效
  3. 移除首页对应逻辑

关键代码:

  1. common.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 添加响应拦截器
// 统一处理token过期
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
return response;
}, function (error) {
// console.dir(error)
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么: 比如统一处理token失效
// 统一处理token失效
if (error.response.status === 401) {
// 弹框提示用户
showToast('请重新登录')
// 删除缓存
localStorage.removeItem('token')
localStorage.removeItem('username')
// 返回登录页
setTimeout(() => {
location.href = 'login.html'
}, 1500)
}
return Promise.reject(error);
});
  1. index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 首页-统计数据
async function getData() {
// 调用接口(登录成功之后才可以调用)
const res = await axios({
url: '/dashboard',
// 通过axios请求拦截器统一携带
})
const overview = res.data.data.overview

// 渲染数据
Object.keys(overview).forEach(key => {
document.querySelector(`.${key}`).innerText = overview[key]
})
}

axios响应拦截器-数据剥离

axios响应拦截器-数据剥离

需求:

  1. axios响应拦截器-数据剥离

  2. 页面中使用数据时少写一个data

image-20230430131930425

image-20230430132025695

核心步骤:

  1. 剥离data属性(响应拦截器)
  2. 调整数据使用逻辑(登录,注册,首页)

关键代码:

  1. commons.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 添加响应拦截器
// 统一处理token过期
// 数据剥离
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么,比如: 数据剥离
// 剥离data属性,页面中少写.data属性,直接可以获取到数据
return response.data;
}, function (error) {
// console.dir(error)
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么: 比如统一处理token失效
// 统一处理token失效
if (error.response.status === 401) {
// 弹框提示用户
showToast('请重新登录')
// 删除缓存
localStorage.removeItem('token')
localStorage.removeItem('username')
// 返回登录页
setTimeout(() => {
location.href = 'login.html'
}, 1500)
}
return Promise.reject(error);
});
  1. index.js:移除多余的.data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 首页-统计数据
async function getData() {
// 调用接口(登录成功之后才可以调用)
const res = await axios({
url: '/dashboard',
// 通过axios请求拦截器统一携带
})
const overview = res.data.overview

// 渲染数据
Object.keys(overview).forEach(key => {
document.querySelector(`.${key}`).innerText = overview[key]
})
}
  1. register.js:移除多余的.data,try
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
document.querySelector('#btn-register').addEventListener('click', async () => {
// 1. 收集并校验数据
const form = document.querySelector('.register-form')
const data = serialize(form, { empty: true, hash: true })
// console.log(data)
const { username, password } = data
console.log(username, password)
// 非空校验
if (username === '' || password === '') {
showToast('用户名和密码不能为空')
return
}

// 长度校验
if (username.length < 8 || username.length > 30 || password.length < 6 || password.length > 30) {
showToast('用户名的长度为8-30,密码的长度为6-30')
return
}

// 2. 数据提交
try {
// .post 请求方法 post,参数1:请求URL,参数2:提交的数据
const res = await axios.post('/register', { username, password })
// console.log(res)
showToast(res.message)
} catch (error) {
// console.dir(error)
showToast(error.response.data.message)
}
})
  1. login.js:移除多余的.data,try
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
document.querySelector('#btn-login').addEventListener('click', async () => {
// 1. 收集并校验数据
const form = document.querySelector('.login-form')
const data = serialize(form, { empty: true, hash: true })
console.log(data)
const { username, password } = data
// 非空判断
if (username === '' || password === '') {
showToast('用户名和密码不能为空')
return
}

// 格式判断
if (username.length < 8 || username.length > 30 || password.length < 6 || password.length > 30) {
showToast('用户名长度8-30,密码长度6-30')
return
}

// 2. 提交数据
try {
const res = await axios.post('/login', { username, password })
// console.log(res)
showToast(res.message)
// 3. 缓存响应数据
localStorage.setItem('token', res.data.token)
localStorage.setItem('username', res.data.username)
// 4. 跳转首页
// 延迟一会在跳转,让提示框显示
setTimeout(() => {
// login.html和index.html的相对关系
location.href = './index.html'
}, 1500)

} catch (error) {
// console.dir(error)
showToast(error.response.data.message)
}

})
作者

步步为营

发布于

2024-05-08

更新于

2025-03-15

许可协议