You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@inlong.apache.org by do...@apache.org on 2022/08/09 09:49:55 UTC

[inlong] branch master updated: [INLONG-5422][Dashboard] Unified authentication logic, including login and logout (#5427)

This is an automated email from the ASF dual-hosted git repository.

dockerzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git


The following commit(s) were added to refs/heads/master by this push:
     new d9af70f76 [INLONG-5422][Dashboard] Unified authentication logic, including login and logout (#5427)
d9af70f76 is described below

commit d9af70f76b8b97f0d6d2da8ee723af404eb12962
Author: Daniel <le...@apache.org>
AuthorDate: Tue Aug 9 17:49:51 2022 +0800

    [INLONG-5422][Dashboard] Unified authentication logic, including login and logout (#5427)
---
 inlong-dashboard/src/components/Icons/logo.svg     | 43 -----------
 .../src/components/Layout/NavWidget/index.tsx      | 15 +---
 inlong-dashboard/src/configs/default/index.ts      |  2 +
 inlong-dashboard/src/configs/routes/index.tsx      |  5 --
 inlong-dashboard/src/locales/cn.json               |  3 -
 inlong-dashboard/src/locales/en.json               |  3 -
 inlong-dashboard/src/models/index.ts               | 10 ++-
 inlong-dashboard/src/pages/Login/index.module.less | 42 +++--------
 inlong-dashboard/src/pages/Login/index.tsx         | 87 +++++++++-------------
 inlong-dashboard/src/router.tsx                    | 36 +++++++--
 inlong-dashboard/src/utils/request.ts              |  5 +-
 11 files changed, 91 insertions(+), 160 deletions(-)

diff --git a/inlong-dashboard/src/components/Icons/logo.svg b/inlong-dashboard/src/components/Icons/logo.svg
deleted file mode 100644
index 0b9ffa114..000000000
--- a/inlong-dashboard/src/components/Icons/logo.svg
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<svg width="559" height="188" viewBox="0 0 559 188" xmlns="http://www.w3.org/2000/svg" overflow="hidden">
-    <defs>
-        <linearGradient x1="21.4018%" y1="6.67873%" x2="90.5742%" y2="88.6401%" id="linearGradient-1">
-            <stop offset="0" stop-color="#FAAD1A"/>
-            <stop offset="1" stop-color="#FF4444"/>
-        </linearGradient>
-    </defs>
-    <g id="login" fill="none" fill-rule="evenodd">
-        <g id="应龙logo设计---优化" transform="translate(-1312 -2996)">
-            <g id="白色" transform="translate(1312 2996)">
-                <g>
-                    <g id="编组-8" fill="#FFFFFF" transform="translate(226 25)">
-                        <path d="M23.3789 40 20.6055 33.3008 7.14844 33.3008 4.41406 40 0.683594 40 12.1289 11.9922 15.4102 11.9922 27.1875 40 23.3789 40ZM13.8281 16.8359 8.45703 30.0781 19.2969 30.0781 13.8281 16.8359ZM47.7734 17.9297C49.8958 17.9297 51.8034 18.418 53.4961 19.3945 55.1888 20.3711 56.5072 21.7155 57.4512 23.4277 58.3952 25.14 58.8672 27.0638 58.8672 29.1992 58.8672 32.4284 57.819 35.1139 55.7227 37.2559 53.6263 39.3978 50.9766 40.4688 47.7734 40.4688 46.1849 40.4688 44.6 [...]
-                              id="Apache"/>
-                        <path d="M7.04688 129 7.04688 67.3828 20.3672 67.3828 20.3672 129 7.04688 129ZM71.5195 67.3828 84.582 67.3828 84.582 129 73.4531 129 45.5234 90.5 45.5234 129 32.4609 129 32.4609 67.3828 44.4922 67.3828 71.5195 104.98 71.5195 67.3828ZM109.953 116.625 133.586 116.625 133.586 129 96.6328 129 96.6328 67.3828 109.953 67.3828 109.953 116.625ZM167.551 130.332C162.91 130.332 158.556 129.508 154.488 127.861 150.421 126.214 146.933 123.973 144.025 121.137 141.118 118.301 13 [...]
-                              id="INLONG"/>
-                    </g>
-                    <path d="M186 76.1942 76.1942 186C69.5136 184.69 63.0882 182.668 57 180.017L180.017 57C182.668 63.0882 184.69 69.5136 186 76.1942ZM184.66 119.198 119.198 184.66C111.275 186.837 102.933 188 94.3186 188 93.1042 188 91.8952 187.977 90.6919 187.931L187.931 90.6919C187.977 91.8952 188 93.1042 188 94.3186 188 102.933 186.837 111.275 184.66 119.198ZM45.6624 175.135C18.298 158.624 0 128.608 0 94.3186 0 42.2279 42.2279 0 94.3186 0 128.608 0 158.624 18.298 175.135 45.6624L162.0 [...]
-                          id="形状结合" fill="url(#linearGradient-1)"/>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>
\ No newline at end of file
diff --git a/inlong-dashboard/src/components/Layout/NavWidget/index.tsx b/inlong-dashboard/src/components/Layout/NavWidget/index.tsx
index 207b5005a..83c1298ee 100644
--- a/inlong-dashboard/src/components/Layout/NavWidget/index.tsx
+++ b/inlong-dashboard/src/components/Layout/NavWidget/index.tsx
@@ -19,7 +19,7 @@
 
 import React, { useState } from 'react';
 import { Dropdown, Menu } from 'antd';
-import { useHistory, useSelector, useDispatch, useRequest } from '@/hooks';
+import { useSelector, useRequest } from '@/hooks';
 import { State } from '@/models';
 import { useTranslation } from 'react-i18next';
 // import { FileTextOutlined } from '@/components/Icons';
@@ -31,8 +31,6 @@ import KeyModal from './KeyModal';
 const Comp: React.FC = () => {
   const { t } = useTranslation();
   const userName = useSelector<State, State['userName']>(state => state.userName);
-  const history = useHistory();
-  const dispatch = useDispatch();
 
   const [createModal, setCreateModal] = useState<Record<string, unknown>>({
     visible: false,
@@ -44,16 +42,7 @@ const Comp: React.FC = () => {
 
   const { run: runLogout } = useRequest('/anno/logout', {
     manual: true,
-    onSuccess: () => {
-      localStorage.removeItem('userName');
-      history.push('/login');
-      dispatch({
-        type: 'setUser',
-        payload: {
-          userName: null,
-        },
-      });
-    },
+    onSuccess: () => (window.location.href = '/'),
   });
 
   const menu = (
diff --git a/inlong-dashboard/src/configs/default/index.ts b/inlong-dashboard/src/configs/default/index.ts
index 570e528ba..b0e25699f 100644
--- a/inlong-dashboard/src/configs/default/index.ts
+++ b/inlong-dashboard/src/configs/default/index.ts
@@ -16,8 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import i18n from '@/i18n';
 
 export const config = {
   title: '',
   logo: '/logo.svg',
+  loginUrl: `${window.location.origin}/#/${i18n?.language || ''}/login`,
 };
diff --git a/inlong-dashboard/src/configs/routes/index.tsx b/inlong-dashboard/src/configs/routes/index.tsx
index 0f1b722bb..d95793d8f 100644
--- a/inlong-dashboard/src/configs/routes/index.tsx
+++ b/inlong-dashboard/src/configs/routes/index.tsx
@@ -26,11 +26,6 @@ export interface RouteProps extends Omit<ReactRouteProps, 'component'> {
 }
 
 const routes: RouteProps[] = [
-  {
-    path: '/',
-    component: () => import('@/pages/Login'),
-    exact: true,
-  },
   {
     path: '/login',
     component: () => import('@/pages/Login'),
diff --git a/inlong-dashboard/src/locales/cn.json b/inlong-dashboard/src/locales/cn.json
index 872a08d27..60e0e6f49 100644
--- a/inlong-dashboard/src/locales/cn.json
+++ b/inlong-dashboard/src/locales/cn.json
@@ -488,9 +488,6 @@
   "pages.Login.PasswordRules": "只能包含小写字母、数字、中划线、下划线",
   "pages.Login.LogIn": "登录",
   "pages.Login.PleaseEnterYourPassword": "请输入密码",
-  "pages.Login.Reset": "请联系管理员重置",
-  "pages.Login.PasswordCanNotBeBlank": "密码不能为空",
-  "pages.Login.UsernameCanNotBeEmpty": "用户名不能为空",
   "pages.UserManagement.DetailModal.Day": "天",
   "pages.UserManagement.DetailModal.New": "新建",
   "pages.UserManagement.DetailModal.UserPassword": "用户密码",
diff --git a/inlong-dashboard/src/locales/en.json b/inlong-dashboard/src/locales/en.json
index 16d70c1c9..e1ad7f273 100644
--- a/inlong-dashboard/src/locales/en.json
+++ b/inlong-dashboard/src/locales/en.json
@@ -488,9 +488,6 @@
   "pages.Login.PasswordRules": "Only lowercase letters, numbers, minus, and underscores",
   "pages.Login.LogIn": "LogIn",
   "pages.Login.PleaseEnterYourPassword": "Please enter your password",
-  "pages.Login.Reset": "Please contact administrator reset",
-  "pages.Login.PasswordCanNotBeBlank": "Password can not be empty",
-  "pages.Login.UsernameCanNotBeEmpty": "Username can not be empty",
   "pages.UserManagement.DetailModal.Day": "Days",
   "pages.UserManagement.DetailModal.New": "New construction",
   "pages.UserManagement.DetailModal.UserPassword": "Password",
diff --git a/inlong-dashboard/src/models/index.ts b/inlong-dashboard/src/models/index.ts
index 3503f5d2b..fed19ff82 100644
--- a/inlong-dashboard/src/models/index.ts
+++ b/inlong-dashboard/src/models/index.ts
@@ -26,6 +26,8 @@ import { getCurrentLocale } from '@/configs/locales';
 export interface State {
   locale: string;
   userName: string;
+  userId: number;
+  roles: string[];
   currentMenu: null | {
     name: string;
     path: string;
@@ -34,15 +36,19 @@ export interface State {
 
 const state: State = {
   locale: getCurrentLocale(),
-  userName: localStorage.getItem('userName') || null,
+  userName: '',
+  userId: 0,
+  roles: [],
   currentMenu: null,
 };
 
 const reducers = {
-  setUser: (state: State, payload) => {
+  setUserInfo: (state: State, payload) => {
     return {
       ...state,
       userName: payload.userName,
+      userId: payload.userId,
+      roles: payload.roles,
     };
   },
 
diff --git a/inlong-dashboard/src/pages/Login/index.module.less b/inlong-dashboard/src/pages/Login/index.module.less
index 13204a819..89d380821 100644
--- a/inlong-dashboard/src/pages/Login/index.module.less
+++ b/inlong-dashboard/src/pages/Login/index.module.less
@@ -38,39 +38,17 @@
 * under the License.
 */
 
-.wrap {
+.containerBg {
+  display: flex;
+  justify-content: center;
+  align-items: center;
   width: 100%;
   height: 100%;
-  background: #223459;
+  background: #f0f2f5;
   overflow: hidden;
+}
 
-  .form-wrap {
-    position: relative;
-    top: 30%;
-    left: 50%;
-    transform: translateX(-50%);
-    width: 20%;
-
-    .logo {
-      position: relative;
-      left: 15%;
-      width: 70%;
-      margin-bottom: 25px;
-    }
-    .title {
-      font-family: PingFangSC-Regular, serif;
-      font-size: 36px;
-      color: #FAFFFB;
-      text-align: center;
-      margin-bottom: 25px;
-    }
-    .button-wrap {
-      width: 100%;
-      text-align: right;
-    }
-    .button-desc {
-      color: #fff;
-      float: left;
-    }
-  }
-}
\ No newline at end of file
+.container {
+  width: 350px;
+  margin-top: -100px;
+}
diff --git a/inlong-dashboard/src/pages/Login/index.tsx b/inlong-dashboard/src/pages/Login/index.tsx
index 694e89f00..fbe72c0a7 100644
--- a/inlong-dashboard/src/pages/Login/index.tsx
+++ b/inlong-dashboard/src/pages/Login/index.tsx
@@ -17,82 +17,67 @@
  * under the License.
  */
 
-import React, { useState } from 'react';
-import { Input, Button } from 'antd';
-import { useDispatch, useHistory, useRequest } from '@/hooks';
+import React from 'react';
+import { Button } from 'antd';
+import { UserOutlined, LockOutlined } from '@ant-design/icons';
 import { useTranslation } from 'react-i18next';
+import { config } from '@/configs/default';
 import FormGenerator, { useForm } from '@/components/FormGenerator';
+import request from '@/utils/request';
 import styles from './index.module.less';
 
 const Comp: React.FC = () => {
   const { t } = useTranslation();
   const [form] = useForm();
-  const dispatch = useDispatch();
-  const history = useHistory();
 
-  const config = [
+  const formConfig = [
     {
-      type: <Input placeholder={t('pages.Login.PleaseEnterUserName')} />,
+      type: 'input',
       name: 'username',
-      rules: [{ required: true, message: t('pages.Login.UsernameCanNotBeEmpty') }],
+      props: {
+        placeholder: t('pages.Login.PleaseEnterUserName'),
+        size: 'large',
+        prefix: <UserOutlined />,
+      },
+      rules: [{ required: true, message: t('pages.Login.PleaseEnterUserName') }],
     },
     {
-      type: <Input.Password placeholder={t('pages.Login.PleaseEnterYourPassword')} />,
+      type: 'password',
       name: 'password',
+      props: {
+        placeholder: t('pages.Login.PleaseEnterYourPassword'),
+        size: 'large',
+        prefix: <LockOutlined />,
+      },
       rules: [
-        { required: true, message: t('pages.Login.PasswordCanNotBeBlank') },
+        { required: true, message: t('pages.Login.PleaseEnterYourPassword') },
         { pattern: /^[0-9a-z_-]+$/, message: t('pages.Login.PasswordRules') },
       ],
     },
   ];
-  const [changedValues, setChangedValues] = useState<Record<string, unknown>>({});
-  const { run: runLogin } = useRequest(
-    {
-      url: `/anno/login`,
-      data: {
-        ...changedValues,
-      },
-      method: 'post',
-    },
-    {
-      manual: true,
-      onSuccess: data => {
-        dispatch({
-          type: 'setUser',
-          payload: {
-            userName: data ? changedValues.username : null,
-          },
-        });
-        localStorage.setItem('userName', changedValues.username + '');
-        history.push('/');
-      },
-    },
-  );
+
   const login = async () => {
-    await form.validateFields();
-    runLogin();
+    const data = await form.validateFields();
+    await request({
+      url: '/anno/login',
+      method: 'POST',
+      data,
+    });
+    window.location.href = '/';
   };
+
   const onEnter = e => {
     if (e.keyCode === 13) login();
   };
 
   return (
-    <div className={styles.wrap} onKeyUp={onEnter}>
-      <div className={styles['form-wrap']}>
-        <img className={styles.logo} src={require('../../components/Icons/logo.svg')} alt="" />
-        <div>
-          <FormGenerator
-            form={form}
-            content={config}
-            onValuesChange={(c, v) => setChangedValues(v)}
-          />
-        </div>
-        <div className={styles['button-wrap']}>
-          <span className={styles['button-desc']}>{t('pages.Login.Reset')}</span>
-          <Button type="primary" onClick={login}>
-            {t('pages.Login.LogIn')}
-          </Button>
-        </div>
+    <div className={styles.containerBg} onKeyUp={onEnter}>
+      <div className={styles.container}>
+        <img src={config.logo} style={{ width: '100%' }} alt={config.title} />
+        <FormGenerator form={form} content={formConfig} />
+        <Button type="primary" onClick={login} style={{ width: '100%' }} size="large">
+          {t('pages.Login.LogIn')}
+        </Button>
       </div>
     </div>
   );
diff --git a/inlong-dashboard/src/router.tsx b/inlong-dashboard/src/router.tsx
index 0cde6b1a2..5f3512ed5 100644
--- a/inlong-dashboard/src/router.tsx
+++ b/inlong-dashboard/src/router.tsx
@@ -21,7 +21,14 @@ import React, { Suspense, lazy, useEffect, useCallback, useState } from 'react';
 import { ConfigProvider, Spin } from 'antd';
 import dayjs from 'dayjs';
 import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
-import { useLocation, useHistory, useDispatch, UseRequestProvider, useSelector } from '@/hooks';
+import {
+  useLocation,
+  useHistory,
+  useDispatch,
+  UseRequestProvider,
+  useSelector,
+  useRequest,
+} from '@/hooks';
 import { PageLoading } from '@ant-design/pro-layout';
 import { Provider } from 'react-redux';
 import Layout from '@/components/Layout';
@@ -72,10 +79,28 @@ const App = () => {
   const dispatch = useDispatch();
 
   const locale = useSelector<State, State['locale']>(state => state.locale);
-  const userName = useSelector<State, State['userName']>(state => state.userName);
 
   const [antdMessages, setAntdMessages] = useState();
 
+  useRequest(
+    {
+      url: '/user/currentUser',
+      method: 'POST',
+    },
+    {
+      onSuccess: result => {
+        dispatch({
+          type: 'setUserInfo',
+          payload: {
+            userName: result.name,
+            userId: result.userId,
+            roles: result.roles,
+          },
+        });
+      },
+    },
+  );
+
   const importLocale = useCallback(async locale => {
     if (!localesConfig[locale]) return;
 
@@ -131,9 +156,8 @@ const App = () => {
 
   return antdMessages ? (
     <ConfigProvider locale={antdMessages} autoInsertSpaceInButton={false}>
-      {userName === null ? (
-        <Route exact path="*" render={() => <Login />} />
-      ) : (
+      <Switch>
+        <Route exact path="/login" render={() => <Login />} />
         <Layout>
           <Suspense fallback={<PageLoading />}>
             <Switch>
@@ -142,7 +166,7 @@ const App = () => {
             </Switch>
           </Suspense>
         </Layout>
-      )}
+      </Switch>
     </ConfigProvider>
   ) : (
     <Spin />
diff --git a/inlong-dashboard/src/utils/request.ts b/inlong-dashboard/src/utils/request.ts
index b3971aa87..019fd6647 100644
--- a/inlong-dashboard/src/utils/request.ts
+++ b/inlong-dashboard/src/utils/request.ts
@@ -21,6 +21,7 @@ import { message as Message } from 'antd';
 import { extend } from 'umi-request';
 import { RequestOptionsInit } from 'umi-request/types';
 import nprogress from 'nprogress';
+import { config } from '@/configs/default';
 import requestConcurrentMiddleware from './requestConcurrentMiddleware';
 
 export interface FetchOptions extends RequestOptionsInit {
@@ -94,8 +95,8 @@ export default function request(_options: RequestOptions | string) {
         // 404, 500, ...
         const { status, message } = data;
         if (status === 403) {
-          localStorage.removeItem('userName');
-          window.location.href = '/';
+          window.location.href = config.loginUrl;
+          return Promise.reject(new Error('not logged in'));
         }
         msg = message || status;
       }