前端存储
localstorage
localStorage 方法存储的数据没有时间限制
存储方式:
以键值对(Key-Value)方式存储,永久存储,永不失效,除非手动删除
常用 API:
getItem // 获取记录
setIten// 保存记录
removeItem //移除记录
clear // 清除记录
sessionstorage
HTML5 本地存储 API 中 localStorage 与 sessionStorage 在使用方法上是相同的
区别在于 sessionStorage 在关闭页面后即被清空,而 localStorage 则会一直保存
项目中使用
存储令牌
- 封装
localStorage
API,暴露提供操作方法
utils.js
添加几个操作方法
// 保存
export const saveAccessToken = (token) => {
localStorage.setItem(MOOSE_REACT_LEARN_ACCESS_TOKEN, token);
};
// 获取
export const getAccessToken = () => {
return localStorage.getItem(MOOSE_REACT_LEARN_ACCESS_TOKEN) || 'MOOSE_REACT_LEARN';
};
// 移除
export const removeAccessToken = () => {
localStorage.removeItem(MOOSE_REACT_LEARN_ACCESS_TOKEN);
};
// 封装参数放到请求头中
export const getAuthorization = () => {
let accessToken = getAccessToken();
return isEmpty(accessToken)
? {}
: {
Authorization: `Bearer ${getAccessToken()}`,
};
};
// 为登录时,重定向到 登录界面
export const redirectLogin = () => {
const { redirect } = getPageQuery(); // Note: There may be security issues, please note
if (window.location.pathname !== '/login' && !redirect) {
removeAccessToken();
history.replace({
pathname: '/login',
search: stringify({
redirect: window.location.href,
}),
});
}
};
- 在登录完成之后,使用
localStorage
保存接口返回的 AccessToken
修改登录接口逻辑
import { saveAccessToken } from '@/utils/utils';
......
*login({ payload }, { call, put }) {
// 提交处理后的参数
const response = yield call(postAccountLogin, params);
yield put({
type: 'changeLoginStatus',
payload: response,
}); // Login successfully
if (response.code === 200) {
// 添加这一行
saveAccessToken(response.data);
const urlParams = new URL(window.location.href);
......
- 每次请求的时候把 accessToken 放到请求头中一起发送
修改 request.js
封装,添加 umi-request
请求拦截器
request.interceptors.request.use(
(url, options) => {
// console.log('request.interceptors.request', options);
// 可以设置一些其他的属性
return {
url,
options: { ...options, headers: { ...options.headers, ...getAuthorization() } },
};
},
{ global: true },
);
修改 SecurityLayout
, userId 和返回到字段一致
修改 AvatarDropdown
,userName和返回到字段一致
刷新令牌
OAuth2.0 密码模式授权,会返回 access_token 和 refresh_token
服务端接口在登录完成后只返回 access_token, refresh_token 保存到服务端(redis)
refresh_token 失效时间设置要比 access_token 长一点,确保使用 refresh_token 能够请求到 access_token
当 access_token 失效,前端使用上一次 access_token 请求 refresh_token,返回 access_token,继续再次请求
参考
API 接口返回 code
需要和前端约定好
- Java 服务接口定义
code
/**
* token
*/
TOKEN_IS_EMPTY(-10101, "token must not be null"),
TOKEN_VALIDATE_FAIL(-10102, "token check fail"),
TOKEN_INVALID(-10103, "invalid access token"),
REFRESH_TOKEN_NOT_EXIST(-10104, "refresh token not exist"),
ACCESS_TOKEN_IS_EMPTY(-10105, "access token is empty"),
- 调用刷新令牌接口
services/token.js
import { getAccessToken } from '@/utils/utils';
import request from '@/utils/request';
export async function postRefreshToken() {
return request('/api/v1/token/refresh', {
method: 'POST',
requestType: 'form',
data: {
accessToken: getAccessToken(),
},
});
}
- 修改
request.js
// 重试函数
const retry = (response, options) => {
return request(response.url, options);
};
request.interceptors.response.use(async (response, options) => {
// 复制上一次请求
const { status } = await response.clone();
if (status === 401) {
const { code: normalCode } = await response.clone().json();
if (normalCode === 401) {
redirectLogin();
return;
}
// -10103 代表当次请求 token 无效
if (normalCode === -10103) {
const { data, code: refreshCode } = await postRefreshToken();
// 当获取刷新令牌返回 code
if (refreshCode === -10101 || refreshCode === -10102 || refreshCode === -10104) {
redirectLogin();
return;
}
saveAccessToken(data);
return retry(response, options);
}
}
return response;
});
这个时候可以在accessToken 时间过期之后,使用 refreshToken 再次获取 accessToken 继续请求
关注公众号 「全栈技术部」
,不断学习更多有趣的技术知识。