Skip to content

Commit

Permalink
feat: 串通登录、回调流程
Browse files Browse the repository at this point in the history
前端
- 登录页支持回调请求
- 登录后jwt写入localstorage
- api请求携带jwt
  • Loading branch information
jorben committed Jul 7, 2024
1 parent 5597de8 commit 1d22894
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 69 deletions.
8 changes: 4 additions & 4 deletions router/api/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"league/service"
)

// GetIndexMenus 获取前台权限范围内的菜单项
func GetIndexMenus(ctx *gin.Context) {
// MenuIndex 获取前台权限范围内的菜单项
func MenuIndex(ctx *gin.Context) {
c := context.CustomContext{Context: ctx}
if menus, err := getMenus(ctx, model.MenuTypeIndex); err == nil {
c.CJSON(errs.Success, menus)
Expand All @@ -18,8 +18,8 @@ func GetIndexMenus(ctx *gin.Context) {
}
}

// GetAdminMenus 获取管理后台权限范围内的菜单项
func GetAdminMenus(ctx *gin.Context) {
// MenuAdmin 获取管理后台权限范围内的菜单项
func MenuAdmin(ctx *gin.Context) {
c := context.CustomContext{Context: ctx}
if menus, err := getMenus(ctx, model.MenuTypeAdmin); err == nil {
c.CJSON(errs.Success, menus)
Expand Down
6 changes: 3 additions & 3 deletions router/api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
"strconv"
)

// GetUserinfo 获取当前用户信息
func GetUserinfo(ctx *gin.Context) {
// UserCurrent 获取当前用户信息
func UserCurrent(ctx *gin.Context) {
c := context.CustomContext{Context: ctx}
strId := ctx.Value("UserId").(string)
userId, err := strconv.ParseUint(strId, 10, 64) // 10进制
if err != nil {
if err != nil || userId == 0 {
c.CJSON(errs.ErrAuthNoLogin)
return
}
Expand Down
6 changes: 3 additions & 3 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ func SetupRouter(s *gin.Engine, feEmbed embed.FS) {
backend.GET("/auth/logout", api.AuthLogout)

// 菜单相关接口
backend.GET("/menu", api.GetIndexMenus)
backend.GET("/menu", api.MenuIndex)

// 用户相关接口
backend.GET("/user/current", api.GetUserinfo)
backend.GET("/user/current", api.UserCurrent)

backendAdmin := backend.Group("/admin")
backendAdmin.GET("/menu", api.GetAdminMenus)
backendAdmin.GET("/menu", api.MenuAdmin)

s.StaticFS("/static", getFileSystem(feEmbed, "web/build/static"))
s.NoRoute(func(ctx *gin.Context) {
Expand Down
20 changes: 12 additions & 8 deletions web/src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import {BrowserRouter as Router, Routes, Route, Navigate} from 'react-router-dom'
import {mainRoutes} from "./routes"
import AdminFrame from "./pages/admin/layout/AdminFrame"
import React from "react";
import ReactDOM from "react-dom/client";
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
} from "react-router-dom";
import { mainRoutes } from "./routes";
import AdminFrame from "./pages/admin/layout/AdminFrame";

const root = ReactDOM.createRoot(document.getElementById('root'));
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Router>
<Routes>
<Route path="/admin/*" element={<AdminFrame/>} />
<Route path="/admin/*" element={<AdminFrame />} />
{mainRoutes.map((route, index) => {
return <Route key={index} {...route} />;
})}
Expand All @@ -18,4 +23,3 @@ root.render(
</Router>
</React.StrictMode>
);

201 changes: 154 additions & 47 deletions web/src/pages/Login.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,160 @@
import React from 'react'
import { Col, Layout, Row, Divider, theme, Space, Button, QRCode, Flex } from 'antd';
import {QqOutlined,GoogleOutlined, GithubOutlined} from '@ant-design/icons'
import Logo from '../components/Logo';
import AdminFooter from './admin/layout/AdminFooter';
import BackgroundImg from '../assets/images/loginbackground2@2x.png'
import React, { useEffect } from "react";
import {
Col,
Layout,
Row,
Divider,
theme,
Space,
Button,
QRCode,
Flex,
Spin,
message,
} from "antd";
import {
QqOutlined,
GoogleOutlined,
GithubOutlined,
LoadingOutlined,
} from "@ant-design/icons";
import Logo from "../components/Logo";
import AdminFooter from "./admin/layout/AdminFooter";
import BackgroundImg from "../assets/images/loginbackground2@2x.png";
import { useLocation, useNavigate } from "react-router-dom";
import ApiClient from "../services/client";

function Login() {
const { token } = theme.useToken();
return (
<Layout style={{width: '100%', minHeight:"100vh", background:"rgba(80,80,80,.2)", alignItems: 'center', justifyContent: "center"}}>
<div style={{
width: "1200px",
height: "680px",
boxShadow: "0 0 12px 12px rgba(10,10,10,0.1)",
background: token.colorBgContainer,
}}>
<Row>
<Col span={8} style={{height:680, backgroundImage:`url(${BackgroundImg})`, backgroundSize: 'cover'}}></Col>
<Col span={16}>
<Row style={{padding:"12px", justifyContent:"center"}}><Logo collapsed={false} theme="light"/></Row>
<Row><Col span={20} offset={2}><Divider style={{margin:"0 0 20px 0"}}/></Col></Row>
<Row>
<Col span={16} offset={4}>
const { token } = theme.useToken();
const location = useLocation();
const navigate = useNavigate();
const searchParams = new URLSearchParams(location.search);
const isCallback = searchParams.get("callback");

const [messageApi, contextHolder] = message.useMessage();

<Row style={{justifyContent:"center", textAlign:"center"}}>
<Flex vertical>
<div><h2>微信登录</h2></div>
<QRCode value="https://league.yation.com/login?type=wechat" size="112" status="loading" />
<div style={{color:"#666", marginTop:"12px"}}><p>使用微信扫一扫登录</p><p>"League"</p></div>
</Flex>
</Row>
<Row>
<Divider style={{margin:"32px 0 24px 0"}} />
<Space>
<span>其他登录方式:</span>
<Button shape="circle" href='/auth/login?type=qq' icon={<QqOutlined />} />
<Button shape="circle" href='/auth/login?type=google' icon={<GoogleOutlined />} />
<Button shape='circle' href='/auth/login?type=github' icon={<GithubOutlined />} />
</Space>

</Row>
</Col>
</Row>
</Col>
useEffect(() => {
if (isCallback) {
const loginCallback = async () => {
ApiClient.get("/auth/callback" + location.search)
.then((response) => {
// console.log("/auth/callback", response.data);
if (response.data?.code === 0) {
// 存储jwt
localStorage.setItem("jwt", JSON.stringify(response.data?.data));
// 跳转页面
// TODO: 支持跳转回登录来源页面,并做同源校验
navigate("/admin");
} else {
messageApi.error(response.data?.message);
}
})
.catch((error) => {
console.log(error);
messageApi.error("获取用户信息失败,请稍后重试!");
});
};
loginCallback();
}
}, [messageApi, isCallback, navigate, location]);

return (
<Layout
style={{
width: "100%",
minHeight: "100vh",
background: "rgba(80,80,80,.2)",
alignItems: "center",
justifyContent: "center",
}}
>
{isCallback ? (
<Spin
size="large"
indicator={<LoadingOutlined spin />}
tip="加载中..."
fullscreen
/>
) : (
<>
<div
style={{
width: "1200px",
height: "680px",
boxShadow: "0 0 12px 12px rgba(10,10,10,0.1)",
background: token.colorBgContainer,
}}
>
<Row>
<Col
span={8}
style={{
height: 680,
backgroundImage: `url(${BackgroundImg})`,
backgroundSize: "cover",
}}
></Col>
<Col span={16}>
<Row style={{ padding: "12px", justifyContent: "center" }}>
<Logo collapsed={false} theme="light" />
</Row>
<Row>
<Col span={20} offset={2}>
<Divider style={{ margin: "0 0 20px 0" }} />
</Col>
</Row>
<Row>
<Col span={16} offset={4}>
<Row
style={{ justifyContent: "center", textAlign: "center" }}
>
<Flex vertical>
<div>
<h2>微信登录</h2>
</div>
<QRCode
value="https://league.yation.com/login?type=wechat"
size="112"
status="loading"
/>
<div style={{ color: "#666", marginTop: "12px" }}>
<p>使用微信扫一扫登录</p>
<p>"League"</p>
</div>
</Flex>
</Row>
<Row>
<Divider style={{ margin: "32px 0 24px 0" }} />
<Space>
<span>其他登录方式:</span>
<Button
shape="circle"
href="http://127.0.0.1:8080/api/auth/login?type=qq"
icon={<QqOutlined />}
/>
<Button
shape="circle"
href="http://127.0.0.1:8080/api/auth/login?type=google"
icon={<GoogleOutlined />}
/>
<Button
shape="circle"
href="http://127.0.0.1:8080/api/auth/login?type=github"
icon={<GithubOutlined />}
/>
</Space>
</Row>
</Col>
</Row>
</div>
<AdminFooter noBackground={true}/>
</Layout>
)
</Col>
</Row>
</div>
<AdminFooter noBackground={true} />
</>
)}
{contextHolder}
</Layout>
);
}

export default Login
export default Login;
7 changes: 3 additions & 4 deletions web/src/services/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ const ApiClient = axios.create({
ApiClient.interceptors.request.use((config) => {
// TODO: 计算csrf token
const csrfToken = "1234";
const jwt =
"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJsZWFndWUiLCJleHAiOjE3MjAyOTIxOTMsIm5iZiI6MTcyMDI4NDY5MywiaWF0IjoxNzIwMjg0OTkzLCJqdGkiOiIxIn0.4Q2L3FfuxrBQ7d05NiY7_dNqZi_ckCM36lv2FSR3YLuUgkTeNrY8Wp5GGxt-GVh6";
const jwt = JSON.parse(localStorage.getItem("jwt"));
if (csrfToken) {
config.headers["X-Csrf-Token"] = csrfToken;
}
if (jwt) {
config.headers["X-Token"] = jwt;
if (jwt?.token) {
config.headers["X-Token"] = jwt?.token;
}
return config;
});
Expand Down

0 comments on commit 1d22894

Please sign in to comment.