feat: Oauth update
This commit is contained in:
@@ -1 +1,32 @@
|
||||
/** @format */
|
||||
|
||||
import web from "@/utils/axios/web.ts";
|
||||
|
||||
/**
|
||||
* 获取所有Minio信息
|
||||
*/
|
||||
export const getAllMinioInfo = () => {
|
||||
return web.request({
|
||||
url: "/oss/minio/getAllMinioInfo",
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 第三方登录
|
||||
* @param type
|
||||
*/
|
||||
export const oauthLogin = (type: string) => {
|
||||
return web.request({
|
||||
url: "/oauth/render/" + type,
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
export const geOauthtUserInfo = () => {
|
||||
return web.request({
|
||||
url: "/oauth/userInfo/",
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
50
src/components/LoadingPage/index.less
Normal file
50
src/components/LoadingPage/index.less
Normal file
@@ -0,0 +1,50 @@
|
||||
.splash-body {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-image: url("@/assets/images/background.png");
|
||||
}
|
||||
.gegga {
|
||||
width: 0;
|
||||
}
|
||||
.snurra {
|
||||
filter: url(#gegga);
|
||||
}
|
||||
.stopp1 {
|
||||
stop-color: #f700a8;
|
||||
}
|
||||
.stopp2 {
|
||||
stop-color: #ff8000;
|
||||
}
|
||||
.halvan {
|
||||
animation: Snurra1 10s infinite linear;
|
||||
stroke-dasharray: 180 800;
|
||||
fill: none;
|
||||
stroke: url(#gradient);
|
||||
stroke-width: 23;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
.strecken {
|
||||
animation: Snurra1 3s infinite linear;
|
||||
stroke-dasharray: 26 54;
|
||||
fill: none;
|
||||
stroke: url(#gradient);
|
||||
stroke-width: 23;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
.skugga {
|
||||
filter: blur(5px);
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
transform: translate(3px, 3px);
|
||||
}
|
||||
@keyframes Snurra1 {
|
||||
0% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: -403px;
|
||||
}
|
||||
}
|
73
src/components/LoadingPage/index.tsx
Normal file
73
src/components/LoadingPage/index.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
/** @format */
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import "./index.less";
|
||||
import { geOauthtUserInfo } from "@/api/user";
|
||||
import localforage from "localforage";
|
||||
|
||||
const LoadingPage: React.FC = () => {
|
||||
async function getUserInfo() {
|
||||
const res: any = await geOauthtUserInfo();
|
||||
localforage.setItem("token", res.data.token).then();
|
||||
localforage.setItem("userId", res.data.userId).then();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.add("loading-body");
|
||||
getUserInfo().then(() => {
|
||||
if (
|
||||
localforage.getItem("token").then() !== null &&
|
||||
localforage.getItem("userId").then() !== null
|
||||
) {
|
||||
window.location.href = "/main";
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
document.body.classList.remove("loading-body");
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<svg className="gegga">
|
||||
<defs>
|
||||
<filter id="gegga">
|
||||
<feGaussianBlur
|
||||
in="SourceGraphic"
|
||||
stdDeviation="7"
|
||||
result="blur"></feGaussianBlur>
|
||||
<feColorMatrix
|
||||
in="blur"
|
||||
mode="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -10"
|
||||
result="inreGegga"></feColorMatrix>
|
||||
<feComposite
|
||||
in="SourceGraphic"
|
||||
in2="inreGegga"
|
||||
operator="atop"></feComposite>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
<svg className="snurra" width="200" height="200" viewBox="0 0 200 200">
|
||||
<defs>
|
||||
<linearGradient id="linjärGradient">
|
||||
<stop className="stopp1" offset="0"></stop>
|
||||
<stop className="stopp2" offset="1"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="160"
|
||||
x2="160"
|
||||
y1="40"
|
||||
x1="40"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="gradient"
|
||||
xlinkHref="#linjärGradient"></linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
className="halvan"
|
||||
d="m 164,100 c 0,-35.346224 -28.65378,-64 -64,-64 -35.346224,0 -64,28.653776 -64,64 0,35.34622 28.653776,64 64,64 35.34622,0 64,-26.21502 64,-64 0,-37.784981 -26.92058,-64 -64,-64 -37.079421,0 -65.267479,26.922736 -64,64 1.267479,37.07726 26.703171,65.05317 64,64 37.29683,-1.05317 64,-64 64,-64"></path>
|
||||
<circle className="strecken" cx="100" cy="100" r="64"></circle>
|
||||
</svg>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default React.memo(LoadingPage);
|
@@ -7,8 +7,8 @@ import Login from "./modules/login/index.ts";
|
||||
import Register from "./modules/register/index.ts";
|
||||
import home from "./modules/home/index.ts";
|
||||
import Main from "./modules/main/index.ts";
|
||||
|
||||
import ComponentLoading from "@/components/ComponentLoading";
|
||||
import Loading from "./modules/loading";
|
||||
|
||||
const routes: RouteObject[] = [
|
||||
{
|
||||
@@ -23,10 +23,6 @@ const routes: RouteObject[] = [
|
||||
path: "/register",
|
||||
Component: (props) => ComponentLoading(Register, props),
|
||||
},
|
||||
// {
|
||||
// path: '/home',
|
||||
// Component: home,
|
||||
// },
|
||||
{
|
||||
path: "/login",
|
||||
Component: (props) => ComponentLoading(Login, props),
|
||||
@@ -35,6 +31,10 @@ const routes: RouteObject[] = [
|
||||
path: "/main",
|
||||
Component: (props) => ComponentLoading(Main, props),
|
||||
},
|
||||
{
|
||||
path: "/loading",
|
||||
Component: (props) => ComponentLoading(Loading, props),
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
11
src/router/modules/loading/index.ts
Normal file
11
src/router/modules/loading/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/** @format */
|
||||
|
||||
import { lazy } from "react";
|
||||
|
||||
const loading = lazy(
|
||||
() =>
|
||||
new Promise((resolve: any) => {
|
||||
setTimeout(() => resolve(import("@/components/LoadingPage")), 0);
|
||||
}),
|
||||
);
|
||||
export default loading;
|
@@ -2,6 +2,7 @@
|
||||
|
||||
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
|
||||
import { message } from "antd";
|
||||
import { DecryptData, EncryptData } from "@/utils/encrypt/encrypt.ts";
|
||||
|
||||
class Request {
|
||||
private instance: AxiosInstance | undefined;
|
||||
@@ -11,6 +12,9 @@ class Request {
|
||||
// 全局请求拦截
|
||||
this.instance.interceptors.request.use(
|
||||
(config) => {
|
||||
if (config.method == "post") {
|
||||
config.data = EncryptData(JSON.stringify(config.data));
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
@@ -20,12 +24,16 @@ class Request {
|
||||
|
||||
// 全局响应拦截
|
||||
this.instance.interceptors.response.use(
|
||||
(res) => {
|
||||
// if (res.data.code && res.data.code !== 200) {
|
||||
// message.error(res.data.message).then()
|
||||
// return Promise.reject(res.data)
|
||||
// }
|
||||
return res.data;
|
||||
(response) => {
|
||||
// 后端返回字符串表示需要解密操作
|
||||
if (typeof response.data == "string") {
|
||||
response.data = DecryptData(response.data);
|
||||
if (!response.data.code && response.data.code !== 200) {
|
||||
message.error(response.data.message).then();
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
(error) => {
|
||||
const { response } = error;
|
||||
|
@@ -1,173 +1,29 @@
|
||||
/** @format */
|
||||
|
||||
import JSEncrypt from "jsencrypt";
|
||||
import CryptoJS from "crypto-js";
|
||||
|
||||
const key = CryptoJS.enc.Hex.parse("d86d7bab3d6ac01ad9dc6a897652f2d2");
|
||||
// const iv = CryptoJS.enc.Latin1.parse("d86d7bab3d6ac01ad9dc6a897652f2d2");
|
||||
|
||||
// 加密
|
||||
export function rsaEncrypt(Str: string, afterPublicKey: string) {
|
||||
const encryptor = new JSEncrypt();
|
||||
encryptor.setPublicKey(afterPublicKey); // 设置公钥
|
||||
return encryptor.encrypt(Str); // 对数据进行加密
|
||||
}
|
||||
|
||||
// 解密
|
||||
export function rsaDecrypt(Str: string, frontPrivateKey: string) {
|
||||
const encryptor = new JSEncrypt();
|
||||
encryptor.setPrivateKey(frontPrivateKey); // 设置私钥
|
||||
return encryptor.decrypt(Str); // 对数据进行解密
|
||||
}
|
||||
|
||||
export function aesEncrypt(aeskey: string, Str: string) {
|
||||
// 设置一个默认值,如果第二个参数为空采用默认值,不为空则采用新设置的密钥
|
||||
const key = CryptoJS.enc.Utf8.parse(aeskey);
|
||||
const srcs = CryptoJS.enc.Utf8.parse(Str);
|
||||
function EncryptData(data: any) {
|
||||
const srcs = CryptoJS.enc.Utf8.parse(data);
|
||||
const encrypted = CryptoJS.AES.encrypt(srcs, key, {
|
||||
// 切记 需要和后端算法模式一致
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
|
||||
return encrypted.toString();
|
||||
}
|
||||
|
||||
export function aesDecrypt(aeskey: string, Str: string) {
|
||||
const key = CryptoJS.enc.Utf8.parse(aeskey);
|
||||
const decrypt = CryptoJS.AES.decrypt(Str, key, {
|
||||
// 切记 需要和后端算法模式一致
|
||||
// 解密
|
||||
function DecryptData(data: any) {
|
||||
// const stime = new Date().getTime();
|
||||
const decrypt = CryptoJS.AES.decrypt(data, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取16位随机码AES
|
||||
* @returns {string}
|
||||
*/
|
||||
export function get16RandomNum() {
|
||||
const chars = [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"O",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"l",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
];
|
||||
let nums = "";
|
||||
//这个地方切记要选择16位,因为美国对密钥长度有限制,选择32位的话加解密会报错,需要根据jdk版本去修改相关jar包,有点恼火,选择16位就不用处理。
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const id = parseInt(String(Math.random() * 61));
|
||||
nums += chars[id];
|
||||
}
|
||||
return nums;
|
||||
}
|
||||
|
||||
//获取rsa密钥对
|
||||
export function getRsaKeys() {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.crypto.subtle
|
||||
.generateKey(
|
||||
{
|
||||
name: "RSA-OAEP",
|
||||
modulusLength: 2048, //can be 1024, 2048, or 4096
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
hash: { name: "SHA-512" }, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
|
||||
},
|
||||
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||
["encrypt", "decrypt"], //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
|
||||
)
|
||||
.then(function (key) {
|
||||
window.crypto.subtle
|
||||
.exportKey("pkcs8", key.privateKey)
|
||||
.then(function (keydata1) {
|
||||
window.crypto.subtle
|
||||
.exportKey("spki", key.publicKey)
|
||||
.then(function (keydata2) {
|
||||
const privateKey = RSA2text(keydata1, 1);
|
||||
|
||||
const publicKey = RSA2text(keydata2);
|
||||
|
||||
resolve({ privateKey, publicKey });
|
||||
})
|
||||
.catch(function (err) {
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function RSA2text(buffer: any, _isPrivate: number = 0) {
|
||||
let binary = "";
|
||||
const bytes = new Uint8Array(buffer);
|
||||
const len = bytes.byteLength;
|
||||
for (let i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
const base64 = window.btoa(binary);
|
||||
const text = base64.replace(/[^\x00-\xff]/g, "$&\x01").replace(/.{64}\x01?/g, "$&\n");
|
||||
|
||||
return text;
|
||||
const result = JSON.parse(CryptoJS.enc.Utf8.stringify(decrypt).toString());
|
||||
// const etime = new Date().getTime();
|
||||
// console.log("DecryptData Time:" + (etime - stime));
|
||||
return result;
|
||||
}
|
||||
export { EncryptData, DecryptData };
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/** @format */
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
// import "./index.less";
|
||||
import "./index.less";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default () => {
|
||||
@@ -17,7 +17,6 @@ export default () => {
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<body translate="no">
|
||||
<div className="container container-star">
|
||||
<div className="star-1"></div>
|
||||
<div className="star-1"></div>
|
||||
@@ -165,7 +164,6 @@ export default () => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -1,10 +1,8 @@
|
||||
/** @format */
|
||||
import { useEffect } from "react";
|
||||
|
||||
import MainContainer from "@/components/Home/main-container/MainContainer.tsx";
|
||||
|
||||
export default () => {
|
||||
useEffect(() => {}, []);
|
||||
return (
|
||||
<div>
|
||||
<MainContainer />
|
||||
|
@@ -20,6 +20,7 @@ import { observer } from "mobx-react";
|
||||
import FooterComponent from "@/components/Footer";
|
||||
import RotateCaptcha, { CaptchaInstance } from "react-rotate-captcha";
|
||||
import { get, load, verify } from "@/api/captcha/index.ts";
|
||||
import { oauthLogin } from "@/api/user";
|
||||
// import useStore from '@/utils/store/useStore.tsx'
|
||||
type LoginType = "account" | "phone";
|
||||
|
||||
@@ -53,6 +54,12 @@ export default observer(() => {
|
||||
key: "phone",
|
||||
},
|
||||
];
|
||||
|
||||
async function oAuthLogin(type: string) {
|
||||
const res: any = await oauthLogin(type);
|
||||
window.location.href = res.data;
|
||||
}
|
||||
|
||||
const [loginType, setLoginType] = useState<LoginType>("account");
|
||||
|
||||
const onSubmit = async (formData: object) => {
|
||||
@@ -359,6 +366,9 @@ export default observer(() => {
|
||||
borderRadius: "50%",
|
||||
}}>
|
||||
<GitlabOutlined
|
||||
onClick={() => {
|
||||
oAuthLogin("GITEE").then();
|
||||
}}
|
||||
style={{ ...iconStyles, color: "#FF6A10" }}
|
||||
/>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user