后台我们采用的是ASP.NET Core Web API,请参见:ASP.NET Core Web API 依赖注入及JWT认证配置
React采用的版本是17.0.2,UI采用的是Ant Design和少量Ant Pro组件。这篇文章我们只写有关Jwt认证的部分。
网络请求库我们采用的是axios。我们稍微封装了一下:
import axios from 'axios'
import { message } from "antd";
import eventBus from './eventBus'
let messageFlag = false;
let navigate: any = "";
// 也可以不使用事件机制,采用其它方式也可以
eventBus.$on((propsObj: any) => {
navigate = propsObj;
}, 'axiosInterceptorsFun');
const goLoginFun = () => {
if (messageFlag === false) {
message.warning("登录已过期,请重新登录!")
messageFlag = true;
setTimeout(() => {
messageFlag = false;
}, 2000);
//navigate是在Layout组件中传递过来的
navigate && navigate('/login');
}
}
const goNoPermission = () => {
navigate && navigate('/nopermission');
}
// 创建axios新实例;如果直接使用axios会导致拦截器在页面刷新以后丢失配置
const axiosInstance = axios.create({ timeout: 60000, withCredentials: true });
axiosInstance.interceptors.request.use(function (request: any) {
const localToken = localStorage.getItem('jwt-token');
if (localToken) {
request.headers['Authorization'] = "Bearer " + localToken;
}
return request;
});
axiosInstance.interceptors.response.use(function (response) {
// 成功时按默认方式处理
// let {status, data: { code } } = response;
// status在200和300(不含300)之间执行then,否则执行catch
return response;
},
function (error) {
if (error.response) {
if (error.response.status === 401) {
// 未登录时,后台返回401
goLoginFun();
}
else if (error.response.status === 403) {
// 已登录,权限不足时,后台返回403
goNoPermission();
}
return Promise.reject(error.response);
}
else {
return Promise.reject(error.message);
}
});
// 登录成功后调用此方法将jwt存入localStorage
export const SaveJwtToLocal = (jwt: string) => {
localStorage.setItem('jwt-token', jwt);
}
// 退出登登录时,调用此方法即可
export const Logout = () => {
localStorage.removeItem('jwt-token');
navigate && navigate('/login');
}
// 用到了ProTable组件,此处忽略
// 将ProTable的params,sort扁平化
export const ParsePagingParams = (params: any, sort: any) => {
const result = { ...params };
if (sort) {
const keys = Object.keys(sort);
if (keys.length > 0) {
result["sortField"] = keys[0];
result["sordDirection"] = sort[keys[0]];
}
}
return result;
}
// 常规的json请求,调用此方法
export const PostJson = (api: string, data: any = {}) => {
return new Promise((resolve, reject) => {
axiosInstance.post("/api/" + api, data).then(res => {
resolve(res);
}, res => {
reject(res);
});
});
}
export const Get = (api: string) => {
return new Promise((resolve, reject) => {
axiosInstance.get("/api/" + api).then(res => {
resolve(res);
}, res => {
reject(res);
});
});
}
其中用到了一个eventbus(懒得写,直接网上复制的;其实也可以不用eventbus,react提供了其它方式也可以实现),代码:
interface eventBusType {
evnetList: Array<any>;
$on: (callbackFun:any, name:string)=>void;
$emit: (sname:string, data:any)=>void;
$remove: (name:string)=>void;
}
const eventBus:eventBusType = {
evnetList: [],
$on(callbackFun, name) {
if (this.evnetList.length > 0 && this.evnetList.find((i:any) => i.name === name)) {
this.evnetList = this.evnetList.filter((i:any) => i.name !== name)
}
this.evnetList = [...this.evnetList, { name, callbackFun }]
},
$emit(name, data= '') {
if (!name) {
return false;
}
this.evnetList.forEach((element:any) => {
if (name === element.name) {
element.callbackFun(data)
}
});
},
$remove(name = "") {
this.evnetList = [...this.evnetList.filter((i:any) => i.name !== name)]
}
}
export default eventBus
另外,layout组件中,触发事件代码:
const navigate = useNavigate();
eventBus.$emit("axiosInterceptorsFun", navigate);
登录页面的主要代码:
const onfinish = async (values: any) => {
await PostJson('authorize/login',{loginId:values.userName,passWord:values.password})
.then((data: any) => {
if (data.data.code === 0 && data.data.data) {
SaveJwtToLocal(data.data.data);
loginStatus = true;
}
else if (data.data.code === 1) {
message.error(data.data.message);
}
}).catch((err) => { console.log("catch:" + err) });
//不能在PostJson的回调函数中使用navigate
if (loginStatus === true) {
navigate("/welcome", { replace: true });
}
}
这个app布局采用的是左侧导航模式,效果如图:
React涉及的东西比较多,比如:布局,路由,状态管理等等。这些可以在实际项目开发中逐步熟悉。