React-登录权限
❤ React-登录权限验证以及axios拦截校验
1、页面搭建
接下来我们开发一下我们的登录界面,并且实现对于我们的接口进行白名单的校验,只有通过白名单的接口,才可以进行自由访问,其他的全部都需要用户登录以后进行访问,否则就跳到用户的登录界面,让用户自己去登录!
(1)先来引入我们的登录界面:
Router之中引入:
const Login = lazy(() => import('@/pages/Login'));
<Route path="/login" element={<Login></Login>}></Route>
(2)页面结构搭建
<div className="backbaige min-h-screen flex items-center justify-center">
<div className="max-w-md w-full p-8 bg-white rounded-lg shadow-lg">
<h2 className="text-2xl font-bold mb-4 text-center">登录</h2>
<div className="mb-4">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<img
className="mx-auto h-10 w-auto"
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
alt="Your Company"
/>
<h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
Login in to your account
</h2>
</div>
</div>
<div className="mb-4">
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form className="space-y-6" action="#" method="POST" onSubmit={handleSubmit}>
<div>
<label htmlFor="username" className="block text-sm font-medium leading-6 text-gray-900">
账号
</label>
<div className="mt-2">
<input
id="username"
name="username"
type="text"
autoComplete="false"
required
value={username}
onChange={handleUsernameChange}
className="forminput block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 pl-1.5"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label htmlFor="password" className="block text-sm font-medium leading-6 text-gray-900">
密码
</label>
</div>
<div className="mt-2">
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
value={password}
onChange={handlePasswordChange}
className="forminput block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 pl-1.5"
/>
</div>
</div>
<div>
<button
type="submit"
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Login in
</button>
</div>
</form>
</div>
</div>
</div>
</div>
(3)完善引入和定义
function Login() {}
import React, { useState } from 'react';
import { login } from '@/api/common/login';
import { Button, Form, Input, Radio, Table, Space, Tag, message, Modal, Popconfirm, Flex } from 'antd';
function Login() {
// 使用 useState 创建状态变量来保存用户输入的用户名、密码和确认密码
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isRegistered, setIsRegistered] = useState(false);
const [age, setAge] = useState(18);
return (结构部分);
}
export default Login;
(4)完善登录的方法
// 处理用户名输入变化的函数
const handleUsernameChange = (event) => {
setUsername(event.target.value);
};
// 处理密码输入变化的函数
const handlePasswordChange = (event) => {
setPassword(event.target.value);
};
// 处理表单提交的函数
const handleSubmit = async (event) => {
event.preventDefault();
// 在实际应用中,这里可以发送注册请求到服务器进行用户注册
// 这里简单地假设密码和确认密码相同才能注册成功
let params={
username,
password,
}
if (username === '' || password === '') {
message.error('用户名和密码不能为空');
return;
}else{
console.log(params, 'params');
try {
const res:any = await login(params);
// const data = response.data;
console.log(res,'res');
if(res.code==200){
message.success(res.message);
}else{
message.error(res.message);
}
} catch (error) {
console.log('获取数据详情失败,请重试!',error);
} finally {
console.log('完!');
}
}
return;
};
接下来我们点击登录:
可以发现,已经实现了登录,并且拿到了token:
2、权限完善
那么问题也来了:
之前我们的接口访问都是任何人都可以进行访问的,相当于是一个开放的权限,极不安全,之前接口的请求头是这样子的:
接下来我们针对接口进行优化,为接口赋值权限,在接口没有token的时候,无法访问
也就是日常我们用户必须登录才能访问的场景
当我们没有登录的时候就会提示我们:
{
"code": 401,
"message": "登录过期,请重新登录!"
}
那么就需要我们对于接口做一些完善和判断
Redux 身份验证和授权
身份验证的整个过程是这样子的:
身份验证和授权是现代 Web 应用程序中不可或缺的部分。只有授权用户才能访问受保护的资源。在前端中,我们通常使用 JWT(JSON Web Tokens)来进行身份验证和授权。
在实现身份验证和授权时,我们需要完成以下几个步骤:
- 用户登录,服务器返回 JWT 令牌
- 将 JWT 令牌存储在本地存储中
- 在每个请求中,将 JWT 令牌作为请求头发送给服务器
- 服务器验证 JWT 令牌,并根据其内容授权或拒绝请求
更改我们的Redux Store,大致的思路我们先理清一下:
我们通过 Redux Store 来管理用户登录的状态。
添加一个名为 auth
的 reducer 来处理用户的登录状态和 Token。
添加一个名为 authMiddleware
的 Redux 中间件来拦截所有非白名单接口的请求,并添加上 Token。
这样子就可以在建立一个权限关卡,只有授权的(也就是携带token的接口)或者白名单之中的才可以直接访问,否则无法访问。
之前我们对于axios的简易封装
// request.js
import axios from 'axios'
const service = axios.create({
baseURL: 'http://localhost:8888', // 设置基础 URL,根据实际情况修改 '/接口前缀', //import.meta.env.VITE_BASE_URL
headers: { 'Content-Type': 'application/json;charset=utf-8', },
timeout: 5000, // 设置请求超时时间
});
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 在请求发送之前做一些处理,例如添加 token 等
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response) => {
// 在响应数据返回之前做一些处理
return response.data;
},
(error) => {
return Promise.reject(error);
}
);
export default service;
授权登录接口对于本地token的存储
更改我们的仓库:
新建一个文件夹,authReducer.js,之中对于我们的授权进行封装
创建了一个 Redux Slice,包含两个 reducers
loginSuccess
和 logoutSuccess
用于更新用户的登录状态和 Token。
接下来我们完善一下更新用户的登录状态和 Token的部分
src\store\reducers\authSlice.js
import { configureStore, createSlice } from '@reduxjs/toolkit';
import thunk from 'redux-thunk';
const authSlice = createSlice({
name: 'auth',
initialState: {
loggedIn: false,
token: null,
},
reducers: {
loginSuccess(state, action) {
state.loggedIn = true;
state.token = action.payload.token;
},
logoutSuccess(state) {
state.loggedIn = false;
state.token = null;
},
},
});
export const { loginSuccess, logoutSuccess } = authSlice.actions;
export default authSlice.reducer;
把授权部分放到我们的根目Reduce之中 src\store\reducers\index.js
import authSlice from './authSlice';
const rootReducer = combineReducers({
menuReducer,
auth: authSlice,
// 可以在这里添加其他的 reducer
});
接下来更改我们的登录部分进行调用 src\pages\Login.tsx
// 引入
import { useDispatch } from 'react-redux';
import { loginSuccess } from '@/store/reducers/authSlice';
const dispatch = useDispatch();
//成功登录以后跳转
const handleSubmit = async (event) => {
event.preventDefault();
// 在实际应用中,这里可以发送注册请求到服务器进行用户注册
// 这里简单地假设密码和确认密码相同才能注册成功
let params={
username,
password,
}
if (username === '' || password === '') {
message.error('用户名和密码不能为空');
return;
}else{
console.log(params, 'params');
try {
const res:any = await login(params);
// const data = response.data;
console.log(res,'res');
if(res.code==200){
message.success(res.message);
// 更新登录状态和 Token
dispatch(loginSuccess({ token: res.data.token }));
// 跳转到主页
window.location.href = '/';
}else{
message.error(res.message);
}
} catch (error) {
console.log('获取数据详情失败,请重试!',error);
} finally {
console.log('完!');
}
}
return;
};
在我们的src\store\reducers\authSlice.js 之中查看结果
loginSuccess(state, action) {
console.log(state, action,'登录行为1');
state.loggedIn = true;
state.token = action.payload.token;
console.log(state,'登录行为2');
},
然而我们发现,在登录行为1之中可完整拿到token,但是行为2之中反而什么都没有
持久化存储
保持用户登录状态的同时将 Token 进行持久化保存
这里我们用浏览器的 LocalStorage 来实现。当用户登录成功后,我们将 Token 存储到 LocalStorage 中;用户注销时,从 LocalStorage 中删除。
更改其实存储的部分:
// 初始状态为显示
const initialState = {
loggedIn: false,
token: localStorage.getItem('token'), // 从 Local Storage 中读取 Token,
};
loginSuccess(state, action) {
console.log(state, action,'登录行为1');
state.loggedIn = true;
state.token = action.payload.token;
console.log(action.payload.token,'登录行为2');
// 将 Token 存储到 Local Storage 中
localStorage.setItem('token', action.payload.token);
console.log(state,'登录行为3');
},
logoutSuccess(state) {
state.loggedIn = false;
state.token = null;
// 从 Local Storage 中删除 Token
localStorage.removeItem('token');
return state;
},
然而输出以后我们发现,存储到localStorage中的token生效了,但是loggedIn依旧未生效。 难道我们要将loggedIn也存储到localStorage之中吗?
我们换种思路:
是不是我们存储的过程出现了问题?
更改我们的存储方式 :
loginSuccess(state, action) {
console.log(state, action,'登录行为1');
localStorage.setItem('token', action.payload.token);
return {
...state,
loggedIn: true,
token:action.payload.token,
};
},
logoutSuccess(state) {
localStorage.removeItem('token');
return {
...state,
loggedIn: false,
token:null,
};
},
3、接口封装
接下来略微完善一下我们之前的axios请求src\utils\request.js
请求拦截器完善
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 在请求发送之前做一些处理,例如添加 token 等
const token = localStorage.getItem('token');
if (token) {
// 如果有 token,则将 Authorization 头部添加到请求中
config.headers.Authorization = `Bearer ${token}`;
}else{
// 跳转到登录页面
window.location.href = '/login';
}
return config;
},
(error) => {
return Promise.reject(error);
// 跳转到前台页面
window.location.href = '/';
}
);
再次请求的时候我们可以发现,已经成功为请求增加了token请求头部
删除token,没有token请求的时候自动跳到登录页面
完善白名单接口
有时候有些接口是白名单接口,这个时候不需要token,我们如何过滤呢?这个时候可以通过白名单接口来进行筛选!
// 白名单接口列表,即不需要进行验证的接口路径
const whiteapilist = [
'/api/login',
'/api/register',
];
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 在请求发送之前做一些处理,例如添加 token 等
const token = localStorage.getItem('token');
const requestPath = config.url;
// 检查请求路径是否在白名单中
if (!whitelist.includes(requestPath) && token) {
// 如果请求不在白名单中且存在 token,则将 Authorization 头部添加到请求中
config.headers.Authorization = `Bearer ${token}`;
}else if(whitelist.includes(requestPath)){
console.log('白名单接口,无需验证');
}else{
// 跳转到登录页面
window.location.href = '/login';
}
return config;
},
(error) => {
return Promise.reject(error);
// 跳转到前台页面
window.location.href = '/';
}
);
- 点赞
- 收藏
- 关注作者
评论(0)