Преглед на файлове

feat: 增加 sso 登录功能,微信登录绑定已经调通

yanglzh преди 1 година
родител
ревизия
74e8434eed
променени са 8 файла, в които са добавени 113 реда и са изтрити 43 реда
  1. 1 0
      package.json
  2. 1 0
      public/imgs/sso/qq.svg
  3. 1 0
      public/imgs/sso/wechat.svg
  4. 8 0
      src/api/system/index.ts
  5. 3 3
      src/router/index.ts
  6. 1 1
      src/router/route.ts
  7. 47 16
      src/views/login/component/account.vue
  8. 51 23
      src/views/sso/index.vue

+ 1 - 0
package.json

@@ -7,6 +7,7 @@
   "scripts": {
     "dev": "vite --force",
     "build": "vite build && npm run getVersion",
+    "build:dev": "vite build --mode development && npm run getVersion",
     "build:golocal": "vite build --mode golocal && npm run getVersion",
     "build:open": "vite build --mode open && npm run getVersion",
     "build:test": "vite build --mode test && npm run getVersion",

+ 1 - 0
public/imgs/sso/qq.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1720709789023" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11495" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M68.399765 590.615655c-37.073602 90.765025-43.465533 176.418105-14.062849 191.757941 20.45478 11.505876 53.692423-14.061849 84.374094-60.084355 11.504876 51.135451 42.186547 95.87897 84.373094 132.952572-44.743519 16.619821-74.146204 44.743519-74.146204 75.42519 0 51.135451 79.259149 93.321998 176.418105 93.321997 88.208052 0 161.07627-33.237643 175.138119-77.982162h20.45478C535.009753 990.751357 607.87897 1023.989 696.087023 1023.989c98.436943 0 176.418105-40.908561 176.418104-93.321997 0-30.68167-29.402684-58.806368-74.146203-75.42519 42.186547-37.073602 72.868217-81.817121 84.374094-132.952572 30.68067 46.022506 62.640327 71.589231 84.373093 60.084355 30.68167-15.339835 24.289739-102.270901-14.061849-191.757941-29.403684-70.311245-69.033258-122.725682-99.714929-134.230558 0-3.835959 1.278986-8.949904 1.278987-14.062849 0-26.845712-7.669918-52.413437-20.454781-72.868217v-5.112945c0-12.783863-2.555973-24.289739-7.669917-34.516629C818.813704 145.736434 701.200968 0 510.722014 0 320.24206 0 202.630323 145.736434 194.959406 329.824457c-5.112945 10.22689-7.669918 21.732767-7.669918 34.516629v5.112945c-12.783863 20.45478-20.45478 46.022506-20.45478 72.869217v14.061849c-28.123698 11.504876-69.032258 62.640327-98.434943 134.230558z" fill="#4CAFE9" p-id="11496"></path></svg>

+ 1 - 0
public/imgs/sso/wechat.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1720709719544" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6900" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M695.296 346.112c11.776 0 23.552 1.024 34.816 2.048-31.232-146.432-187.904-254.976-366.592-254.976-199.68 0-363.52 136.192-363.52 308.736 0 99.84 54.272 181.76 145.408 245.248l-36.352 109.056L236.032 692.736c45.568 9.216 81.92 18.432 127.488 18.432 11.264 0 22.528-0.512 33.792-1.536-7.168-24.064-11.264-49.664-11.264-76.288 0.512-158.208 136.704-287.232 309.248-287.232zM497.664 240.64c31.232 0 56.32 25.088 56.32 56.32s-25.088 56.32-56.32 56.32-56.32-25.088-56.32-56.32 25.088-56.32 56.32-56.32zM243.2 353.792c-31.232 0-56.32-25.088-56.32-56.32s25.088-56.32 56.32-56.32 56.32 25.088 56.32 56.32-25.088 56.32-56.32 56.32zM1024.512 630.784c0-145.408-145.408-263.68-308.736-263.68-173.056 0-309.248 118.272-309.248 263.68s136.192 263.68 309.248 263.68c36.352 0 72.704-9.216 109.056-18.432l99.84 54.784-27.136-90.624c72.704-54.784 126.976-127.488 126.976-209.408z m-403.456-40.96c-22.016 0-39.936-17.92-39.936-39.936s17.92-39.936 39.936-39.936 39.936 17.92 39.936 39.936-17.92 39.936-39.936 39.936z m199.68 2.56c-22.016 0-39.936-17.92-39.936-39.936s17.92-39.936 39.936-39.936 39.936 17.92 39.936 39.936-17.92 39.936-39.936 39.936z" fill="#69BB64" p-id="6901"></path></svg>

+ 8 - 0
src/api/system/index.ts

@@ -2,6 +2,14 @@ import { get, post, del, put, file } from '/@/utils/request';
 
 export default {
   sysinfo: () => get('/sysinfo'),
+  sso: {
+    list: () => get(`/oauth/provider`),
+    login: (provider: string) => get(`/oauth/login`, { provider }),
+    // sso授权登录回调
+    callback: (params: object) => get(`/oauth/callback`, params),
+    // 系统用户登录绑定授权用户
+    binding: (data: any) => post(`/oauth/binding`, data),
+  },
   login: {
     login: (data: object) => post('/login', data),
     currentUser: () => get('/system/user/currentUser'),

+ 3 - 3
src/router/index.ts

@@ -1,4 +1,4 @@
-import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
+import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
 import NProgress from 'nprogress';
 import 'nprogress/nprogress.css';
 import { store } from '/@/store/index';
@@ -9,7 +9,7 @@ import { initFrontEndControlRoutes } from '/@/router/frontEnd';
 import { initBackEndControlRoutes } from '/@/router/backEnd';
 import { getToken } from "/@/utils/auth";
 
-const whiteList = ['/login', '/sso/gitee']
+const whiteList = ['/login', '/ssoBack']
 
 /**
  * 创建一个可以被 Vue 应用程序使用的路由实例
@@ -17,7 +17,7 @@ const whiteList = ['/login', '/sso/gitee']
  * @link 参考:https://next.router.vuejs.org/zh/api/#createrouter
  */
 const router = createRouter({
-	history: createWebHashHistory(),
+	history: createWebHistory(),
 	routes: staticRoutes,
 });
 

+ 1 - 1
src/router/route.ts

@@ -105,7 +105,7 @@ export const staticRoutes: Array<RouteRecordRaw> = [
 		},
 	},
 	{
-		path: '/sso/:type',
+		path: '/ssoBack',
 		name: 'sso',
 		component: () => import('/@/views/sso/index.vue'),
 		meta: {

+ 47 - 16
src/views/login/component/account.vue

@@ -44,9 +44,10 @@
 				<span>{{ $t('message.account.accountBtnText') }}</span>
 			</el-button>
 		</el-form-item>
-		<!-- <el-form-item class="login-animation4">
-			<img src="/@/assets/gitee.svg" alt="" class="gitee" @click="authLogin('gitee')">
-			<img src="/@/assets/gitee.svg" alt="" class="gitee" @click="authLogin('qq')">
+		<!-- <el-form-item class="login-animation4" v-if="from !== 'sso'">
+			<div class="ssolist">
+				<img class="ssologo" :src="item.logo" v-for="item in ssoList" :key="item.name" @click="authLogin(item.name)">
+			</div>
 		</el-form-item> -->
 		<changePwd ref="changePwdRef"></changePwd>
 	</el-form>
@@ -66,6 +67,7 @@ import { formatAxis } from '/@/utils/formatTime';
 import { encrypt } from '/@/utils/rsa'
 import api from '/@/api/system';
 import { setToken } from "/@/utils/auth";
+import getOrigin from '/@/utils/origin'
 
 // 是否是开源版本
 const ISOPEN = import.meta.env.VITE_ISOPEN
@@ -75,7 +77,14 @@ export default defineComponent({
 	components: {
 		changePwd
 	},
-	setup() {
+	props: {
+		// sso 登录来源
+		from: String,
+		ssoInfo: Object,
+	},
+	setup(props) {
+		console.log(props.from)
+		console.log(props.ssoInfo)
 		const changePwdRef = ref();
 		const { t } = useI18n();
 		const store = useStore();
@@ -90,6 +99,7 @@ export default defineComponent({
 				captcha: '',
 				VerifyKey: '',
 			},
+			ssoList: [] as any[],
 			formRules: {
 				userName: [{ required: true, trigger: 'blur', message: '用户名不能为空' }],
 				password: [{ required: true, trigger: 'blur', message: '密码不能为空' }],
@@ -100,6 +110,11 @@ export default defineComponent({
 			},
 			captchaSrc: '',
 		});
+
+		api.sso.list().then((res: any) => {
+			state.ssoList = res.providers
+		})
+
 		onMounted(() => {
 			getCaptcha();
 			// api.login.ssoList()
@@ -117,18 +132,16 @@ export default defineComponent({
 		};
 
 		function authLogin(type: string) {
-			if (type === 'gitee') {
-				const client_id = 'a0585ded445f240f2adc7957989bdd644fa2cdf0db7d98b0a940ec92df6a0934'
-				const redirect_uri = 'http://localhost:8888/#/sso/gitee'
-				window.open(`https://gitee.com/oauth/authorize?client_id=${client_id}&redirect_uri=${encodeURIComponent(redirect_uri)}&response_type=code`)
-				return
-			}
-			if (type === 'qq') {
-				const client_id = 'a0585ded445f240f2adc7957989bdd644fa2cdf0db7d98b0a940ec92df6a0934'
-				const redirect_uri = 'http://localhost:8888/#/sso/gitee'
-				window.open(`https://gitee.com/oauth/authorize?client_id=${client_id}&redirect_uri=${encodeURIComponent(redirect_uri)}&response_type=code`)
-				return
-			}
+			window.open(getOrigin(import.meta.env.VITE_API_URL + '/oauth/login?provider=' + type))
+			// if (type === 'gitee') {
+			// 	const client_id = 'a0585ded445f240f2adc7957989bdd644fa2cdf0db7d98b0a940ec92df6a0934'
+			// 	const redirect_uri = 'http://localhost:8888/#/sso/gitee'
+			// 	window.open(`https://gitee.com/oauth/authorize?client_id=${client_id}&redirect_uri=${encodeURIComponent(redirect_uri)}&response_type=code`)
+			// 	return
+			// }
+			// if (type === 'qq') {
+			// api.sso.login('qq')
+			// }
 		}
 
 		// 登录
@@ -170,6 +183,12 @@ export default defineComponent({
 								// 存储用户信息到浏览器缓存
 								Session.set('userInfo', userInfos);
 
+								// 如果来自 sso,则直接绑定
+								if (props.from === 'sso') {
+									api.sso.binding(props.ssoInfo).then((res: any) => {
+										ElMessage.success('绑定成功')
+									})
+								}
 
 								// 获取权限配置,上传文件类型等
 								const [columnRes, buttonRes, uploadFileRes] = await Promise.all([api.getInfoByKey('sys.column.switch'), api.getInfoByKey('sys.button.switch'), api.getInfoByKey('sys.uploadFile.way')])
@@ -306,4 +325,16 @@ export default defineComponent({
 		margin-top: 15px;
 	}
 }
+
+.ssolist {
+	display: flex;
+	align-items: center;
+
+	.ssologo {
+		width: 40px;
+		height: 40px;
+		margin-right: 10px;
+		cursor: pointer;
+	}
+}
 </style>

+ 51 - 23
src/views/sso/index.vue

@@ -1,13 +1,18 @@
 <template>
 	<div class="login-container flex-row">
-		<div class="part">
-			<div class="title">SSO</div>
+		<div class="part" v-if="userInfo.avatarUrl">
+			<div class="user">
+				<img :src="userInfo.avatarUrl" alt="" class="avatar">
+				<div class="name">{{ userInfo.nickname }}</div>
+				<div class="info">登录成功即可绑定</div>
+			</div>
+			<Account from="sso" :ssoInfo="userInfo" />
 		</div>
 	</div>
 </template>
 
 <script lang="ts">
-import { computed, defineComponent, getCurrentInstance } from 'vue';
+import { computed, defineComponent, getCurrentInstance, ref } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { useStore } from '/@/store/index';
 import dayjs from 'dayjs';
@@ -15,9 +20,9 @@ import api from '/@/api/system';
 import { Session, Local } from '/@/utils/storage';
 import { initFrontEndControlRoutes } from '/@/router/frontEnd';
 import { initBackEndControlRoutes } from '/@/router/backEnd';
-import { formatAxis } from '/@/utils/formatTime';
 import { ElMessage } from 'element-plus';
 import { setToken } from "/@/utils/auth";
+import Account from '/@/views/login/component/account.vue';
 
 // 定义接口来定义对象的类型
 interface LoginState {
@@ -27,6 +32,7 @@ interface LoginState {
 
 export default defineComponent({
 	components: {
+		Account
 	},
 	data: function () {
 		return {
@@ -50,34 +56,31 @@ export default defineComponent({
 		const store = useStore();
 		const { proxy } = getCurrentInstance() as any;
 
-		// 时间获取
-		const currentTime = computed(() => {
-			return formatAxis(new Date());
-		});
+		const userInfo = ref<any>({})
 
-		api.login.oauth({
-			code: location.search.split('=')[1],
-			types: route.params.type,
-			state: ''
-		}).then(async (res: any) => {
+		api.sso.callback(route.query).then(async (res: any) => {
+		
+			if (!res.loginUser) {
+				userInfo.value = res.oauthUser
+				return
+			}
 
-			setToken(res.token);
-			localStorage.setItem('token', res.token);
-			const userInfos = res.userInfo;
+			setToken(res.loginUser.token);
+			localStorage.setItem('token', res.loginUser.token);
+			const userInfos = res.loginUser;
 			userInfos.avatar = proxy.getUpFileUrl(userInfos.avatar);
 			// 存储 token 到浏览器缓存
 			Local.set('userInfo', userInfos);
 			// 存储用户信息到浏览器缓存
 			Session.set('userInfo', userInfos);
 
-
 			// 获取权限配置,上传文件类型等
-			// const [columnRes, buttonRes, uploadFileRes] = await Promise.all([api.getInfoByKey('sys.column.switch'), api.getInfoByKey('sys.button.switch'), api.getInfoByKey('sys.uploadFile.way')])
+			const [columnRes, buttonRes, uploadFileRes] = await Promise.all([api.getInfoByKey('sys.column.switch'), api.getInfoByKey('sys.button.switch'), api.getInfoByKey('sys.uploadFile.way')])
 
-			// const isSecurityControlEnabled = sessionStorage.isSecurityControlEnabled || null
-			// localStorage.setItem('btnNoAuth', (isSecurityControlEnabled && Number(buttonRes?.data?.configValue)) ? '' : '1');
-			// localStorage.setItem('colNoAuth', (isSecurityControlEnabled && Number(columnRes?.data?.configValue)) ? '' : '1');
-			// localStorage.setItem('uploadFileWay', uploadFileRes?.data?.configValue || '0');
+			const isSecurityControlEnabled = sessionStorage.isSecurityControlEnabled || null
+			localStorage.setItem('btnNoAuth', (isSecurityControlEnabled && Number(buttonRes?.data?.configValue)) ? '' : '1');
+			localStorage.setItem('colNoAuth', (isSecurityControlEnabled && Number(columnRes?.data?.configValue)) ? '' : '1');
+			localStorage.setItem('uploadFileWay', uploadFileRes?.data?.configValue || '0');
 
 			await store.dispatch('userInfos/setUserInfos', userInfos);
 
@@ -126,12 +129,37 @@ export default defineComponent({
 			ElMessage.success('登录成功');
 		};
 
-		return {};
+		return {
+			userInfo
+		};
 	},
 });
 </script>
 
 <style scoped lang="scss">
+.user {
+	text-align: center;
+
+	.avatar {
+		width: 80px;
+		height: 80px;
+		border-radius: 50%;
+	}
+
+	.name {
+		font-size: 20px;
+		font-weight: 500;
+		text-align: center;
+		font-size: #999;
+	}
+
+	.info {
+		font-size: 16px;
+		text-align: center;
+		color: #f40;
+	}
+}
+
 html[data-theme='dark'] {
 	.login-container {
 		background: #293146;