提交 7021f4a3 authored 作者: Your Name's avatar Your Name

fix(卫士通): 单点登录功能对接

上级 2e46eacd
VITE_DEV_API="/mock" VITE_DEV_API="/api"
VITE_PRD_API="/mock" VITE_PRD_API="/"
VITE_SYSTEM_NAMR="创新示范绩效评价" VITE_SYSTEM_NAMR="创新示范绩效评价"
\ No newline at end of file
...@@ -2,11 +2,7 @@ ...@@ -2,11 +2,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<AppHead />
<div class="main-warpper">
<router-view> </router-view> <router-view> </router-view>
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
...@@ -44,8 +40,5 @@ export default defineComponent({ ...@@ -44,8 +40,5 @@ export default defineComponent({
min-width: 1920px; min-width: 1920px;
box-sizing: border-box; box-sizing: border-box;
} }
.main-warpper {
flex: 1;
box-sizing: border-box;
}
</style> </style>
...@@ -5,12 +5,50 @@ import { ...@@ -5,12 +5,50 @@ import {
DataCenterRouter DataCenterRouter
} from "./dataCenter" } from "./dataCenter"
/**
* 基础路由
*/
export enum BaseRouteRouter {
LOGIN = "/login",
SSO = '/sso',
NOT_FIND="/404"
}
export const routes: any[] = [ export const routes: any[] = [
...dataCenter, ...dataCenter,
{ {
path: "/:pathMatch(.*)*", path: "/",
redirect: DataCenterRouter.DATA_MAP, redirect: DataCenterRouter.DATA_MAP,
}, },
{
path: "/index",
redirect: DataCenterRouter.DATA_MAP,
},
{
path: BaseRouteRouter.LOGIN,
meta: {
label: "登录",
},
name: "login",
component: () => import("@/views/login/index.vue")
},
{
path: BaseRouteRouter.SSO,
meta: {
label: "卫士通",
},
name: "sso",
component: () => import("@/views/sso/index.vue")
},
{
path: "/:pathMatch(.*)*",
meta: {
label: "404",
},
name: "404",
component: () => import("@/views/404/index.vue")
},
] ]
const router = createRouter({ const router = createRouter({
......
import {Router} from "vue-router" import {Router} from "vue-router"
import { TOKEN_KEY } from "@/utils/enum/token.ts";
export default (router: Router) => { export default (router: Router) => {
const token = localStorage.getItem("token") const token = localStorage.getItem(TOKEN_KEY)
// router.beforeEach((to, from, next) => { // router.beforeEach((to, from, next) => {
// // if(!token) { // // if(!token) {
// // to.path === "/login" ? next() : router.replace("/login"); // // to.path === "/login" ? next() : router.replace("/login");
......
export const TOKEN_KEY = "cxsf_echartd_token"
\ No newline at end of file
import request from '@/axios'
//单点登录校验
export const getLogin = async () => {
return request({
url: "/user/generatorChallenge"
})
}
/**
* 登录系统
* @param {*} challenge // getLogin 接口返回的
* @param {*} ticket // webSokect 成功后返回的
* @param {*} type // 0|1 平台端|企业端
* @param {*} flag // false 卫士通登录
* @returns
*/
export const postLogin = async (challenge:string, ticket:string) => {
return request({
url: "/sso",
method: "post",
data: {
challenge,
ticket,
type: 0,
flag: false
}
})
}
\ No newline at end of file
import wstVaildate from "./wst"
import {ElMessage, ElLoading } from "element-plus";
import { getLogin, postLogin } from "./api"
export const SINGLE_LOGIN_KEY = "mcj_platform_single_login"
const OPTION_LOADING = {
// 声明一个loading对象
lock: true, // 是否锁屏
text: "正在检查key,请稍后...", // 加载动画的文字
spinner: "el-icon-loading", // 引入的loading图标
background: "rgba(0, 0, 0, 0.5)", // 背景颜色
target: ".sub-main", // 需要遮罩的区域
body: true,
customClass: "mask", // 遮罩层新增类名
}
// 单点登录校验
const singleLogin = async () => {
return new Promise(async (resolve, reject) => {
const loading = ElLoading.service(OPTION_LOADING);
try {
getLogin().then(async (res) => {
const { data } = res || {}
if (!data) {
loading.close();
ElMessage.error("身份校验失败");
return resolve(true)
}
const { result = false, params = {} } = await wstVaildate(data);
const { challenge, ticket } = params;
if (result) {
postLogin(challenge, ticket).then((res) => {
resolve(true)
}).catch(() => {
reject()
})
}
}).catch(() => {
reject()
})
} catch (error) {
console.log("error:", error);
reject()
} finally {
loading.close();
}
})
}
export default singleLogin
\ No newline at end of file
import {
ElMessage
} from "element-plus";
const WEB_SOCKET_URL = "ws://127.0.0.1:36107"
const bl = async (L) => {
var l = "";
for (let i = 0; i < L; i++) {
l = l + "0";
}
return l;
}
//计算body长度
const sendlen = async (str) => {
var s = 0;
for (var i = 0; i < str.length; i++) {
if (str.charAt(i).match(/^[\u4e00-\u9fa5]+$/)) {
s += 3;
} else {
s++;
}
}
s = String(s);
var l = s.length;
var L = 10 - l;
var bodylen = await bl(L) + s;
return bodylen;
}
const xmlStrToXmlObj = async (xmlStr) => {
var xmlObj = {};
if (document.all) {
var xmlDom = new ActiveXObject("Microsoft.XMLDOM");
xmlDom.loadXML(xmlStr);
xmlObj = xmlDom;
} else {
xmlObj = new DOMParser().parseFromString(xmlStr, "text/xml");
}
return xmlObj;
}
const xmlObjToJsonObj = (xmlNodes) => {
var obj = {};
if (xmlNodes.length == 0) {
obj = "";
} else {
for (var i = 0; i < xmlNodes.length; i++) {
var node = xmlNodes[i];
if (typeof node.tagName == "undefined" || node.nodeName == "#text") {
obj = node.nodeValue;
} else {
var key = node.tagName;
var value = xmlObjToJsonObj(node.childNodes);
obj[key] = value;
}
}
}
return obj;
}
// xml数据格式转换
const xmlStrToJsonObj = async (xmlStr) => {
const xmlObj = await xmlStrToXmlObj(xmlStr);
let jsonObj = {};
if (xmlObj.childNodes.length > 0) {
jsonObj = await xmlObjToJsonObj(xmlObj.childNodes);
}
return jsonObj;
}
// 建立webSokect连接
const webSokect = async (randSign, webSocketUrl) => {
if (!randSign || !webSocketUrl) {
return {
result: false
}
}
return new Promise(async (resolve, reject) => {
const bodyx =
`<body><plain><info><random>${randSign}</random></info></plain></body>`;
const bodylen = await sendlen(bodyx);
//套接字单点登录
const ws = new WebSocket(webSocketUrl);
ws.onopen = async function (event) {
ws.send(
"<head><bsid> ml0305c</bsid><bodylen>" +
bodylen +
"</bodylen><digest_id> </digest_id><cipher_id> </cipher_id></head>" +
bodyx +
""
);
};
// websokect 错误处理
ws.onerror = async (event) => {
reject(new Error("websokect连接错误"))
};
ws.onmessage = async function (event) {
let resStr = event.data;
//在外面包了一层他反过来的结构不标准
resStr = `<cush>${resStr}</cush>`;
const jsonObj = await xmlStrToJsonObj(resStr);
const {
errcode,
appendix,
errstr
} = jsonObj.cush.body.plain || {}
if (errcode == "0") {
resStr = appendix
} else {
ElMessage.error(errstr)
reject(new Error("onmessage响应,errorCode命中"))
}
if ("generate random2 error" == resStr) {
reject(new Error("generate random2 error"))
} else {
//要求后进行验证
resolve({
result: true,
params: {
challenge: randSign,
ticket: resStr
}
})
}
};
})
}
/**
* 卫士通验证
*/
const wstVaildate = async (randSign) => {
return webSokect(randSign, WEB_SOCKET_URL)
}
export default wstVaildate
\ No newline at end of file
<template>
404
</template>
\ No newline at end of file
<template> <template>
<router-view></router-view> <AppHead />
<div class="main-warpper">
<router-view> </router-view>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from "vue";
/** /**
* 数据中心模块 * 数据中心模块
*/ */
export default defineComponent({ export default defineComponent({
setup() { setup() {},
});
},
})
</script> </script>
<style lang="scss" scoped>
.main-warpper {
flex: 1;
box-sizing: border-box;
}
</style>
<template>
<div class="login">
<div class="main">
<!-- logo -->
<img src="../../static/img/img1.png" alt class="img1" />
<!-- 中间插图 -->
<img src="../../static/img/img2.png" alt class="img2" />
<!-- 标题 -->
<h5>浙里军地一体化专题门户</h5>
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<h2 class="title">欢迎登录</h2>
<!-- 用户名 -->
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
type="text"
auto-complete="off"
@focus="isUsername = true"
@blur="isUsername = false"
placeholder="用户名"
>
</el-input>
</el-form-item>
<!-- 密码 -->
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="密码"
@focus="isPassword = true"
@blur="isPassword = false"
@keyup.enter.native="handleLogin"
>
</el-input>
</el-form-item>
<!-- 登录按钮 -->
<el-checkbox
v-model="loginForm.rememberMe"
style="margin: 0px 0px 35px 0px"
>记住密码</el-checkbox
>
<el-form-item style="width: 100%">
<el-button
:loading="loading"
size="medium"
type="primary"
style="width: 100%"
@click="handleLogin"
>
{{ loading ? "登 录 中..." : "登 录" }}
</el-button>
<el-button
size="medium"
style="width: 100%; margin-left: 0px"
@click="wstLogin"
>
统一身份登录
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { getToken } from "./service";
import singleLogin from "@/utils/singleLogin/index";
import { TOKEN_KEY } from "@/utils/enum/token";
import { defineComponent } from "vue";
export default defineComponent({
name: "Login",
data() {
return {
wstLoading: false,
isUsername: false,
isPassword: false,
loginForm: {
username: "",
password: "",
rememberMe: false,
code: "",
uuid: "",
type: 1,
},
loginRules: {
username: [
{ required: true, trigger: "blur", message: "用户名不能为空" },
],
password: [
{ required: true, trigger: "blur", message: "密码不能为空" },
],
code: [
{ required: true, trigger: "change", message: "验证码不能为空" },
],
},
loading: false,
};
},
watch: {},
created() {},
methods: {
wstLogin() {
this.wstLoading = true;
singleLogin()
.then(async () => {
const { data } = await getToken();
localStorage.setItem(TOKEN_KEY, data);
this.$router.push("/");
})
.catch(() => {
this.$message.error("身份验证失败");
})
.finally(() => {
this.wstLoading = false;
});
},
handleLogin() {
const loginForm = this.loginForm;
login(loginForm.username, loginForm.password)
.then(async (res) => {
const { data } = await getToken();
localStorage.setItem(TOKEN_KEY, data);
this.$router.push("/");
})
.catch((err) => {
this.$message.error("用户名或密码错误!");
});
},
},
});
</script>
<style lang="scss" scoped>
.login {
background-image: url(../../static/img/login_bgi.png);
background-repeat: no-repeat;
background-size: 100% 100%;
width: 100%;
height: 100vh;
min-height: 810px;
min-width: 1050px;
font-size: 0.16rem;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.main {
position: relative;
width: 940px;
height: 495px;
background: #407ef2;
border-radius: 12px;
box-shadow: 0px 2px 12px 0px rgba(80, 15, 180, 1);
color: rgba(255, 255, 255, 1);
.img1 {
position: absolute;
top: 55px;
left: 72px;
width: 30px;
}
.img2 {
position: absolute;
top: 138px;
left: 86px;
width: 384px;
height: 335px;
}
h3 {
margin-left: 52px;
margin-top: 383px;
margin-bottom: 0;
font-size: 32px;
}
h5 {
margin-left: 115px;
margin-top: 52px;
font-size: 24px;
font-weight: 600;
}
.login-form {
position: absolute;
right: 0;
top: 0;
border-radius: 6px;
background: #ffffff;
width: 299px;
height: 404px;
padding: 58px 33px 33px 33px;
.title {
width: 85px;
font-size: 20px;
margin: 12px auto 56px auto;
border-bottom: 3px #1790ff ridge;
border-radius: 2px;
text-align: center;
color: #333333;
padding-bottom: 8px;
}
.el-input {
height: 32px;
input:focus {
color: rgba(6, 104, 185, 1);
}
input {
outline: none;
height: 38px;
border-top: 0;
border-left: 0;
border-right: 0;
border-radius: 0;
}
}
.color-class {
color: rgba(6, 104, 185, 1);
}
.input-icon {
height: 39px;
width: 19px;
margin-left: 2px;
}
}
}
}
</style>
import request from "@/axios";
//token
export const getToken = async () => {
return request({
url: "/user/getCurrentUser",
method: "get",
})
}
/**
* 登录
* @param username
* @param password
*/
export const login = async (username: string, password: string) => {
request({
url: "",
method: "POST",
data: {
username,
password
}
})
}
\ No newline at end of file
<template>
<div v-loading="wstLoading" class="login1">
<div class="main">
<img src="../assets/img/login_bg.jpg" alt class="img1" />
</div>
</div>
</template>
<script>
import singleLogin from "@/utils/singleLogin/index";
import { getToken } from "./service";
import { TOKEN_KEY } from "@/utils/enum/token";
export default {
name: "sso",
data() {
return {
wstLoading: false,
};
},
watch: {},
created() {
this.wstLogin();
},
mounted() {
},
methods: {
wstLogin() {
this.wstLoading = true;
singleLogin()
.then(async() => {
const { data } = await getToken();
localStorage.setItem(TOKEN_KEY, data);
this.$router.push("/");
})
.catch(() => {
this.$message.error("身份验证失败");
this.$router.push("/login");
})
.finally(() => {
this.wstLoading = false;
});
},
},
};
</script>
<style lang="scss" scoped>
.login1 {
width: 100%;
height: 100%;
.main {
width: 100%;
height: 100%;
.img1 {
width: 100%;
height: 100%;
}
}
}
</style>
import request from "@/axios";
//token
export const getToken = async () => {
return request({
url: "/user/getCurrentUser",
method: "get",
})
}
/**
* 登录
* @param username
* @param password
*/
export const login = async (username: string, password: string) => {
request({
url: "",
method: "POST",
data: {
username,
password
}
})
}
\ No newline at end of file
...@@ -7,7 +7,14 @@ import { resolve } from 'path' ...@@ -7,7 +7,14 @@ import { resolve } from 'path'
export default defineConfig({ export default defineConfig({
server:{ server:{
host:'0.0.0.0' host:'0.0.0.0',
proxy: { // 配置域名代理
"/api": {
target: "http://192.168.0.100:9064",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
}, },
resolve: { resolve: {
alias: { alias: {
...@@ -29,13 +36,5 @@ export default defineConfig({ ...@@ -29,13 +36,5 @@ export default defineConfig({
// delay: [0, 200], // delay: [0, 200],
// }), // }),
], ],
// server: {
// proxy: { // 配置域名代理
// "/api": {
// target: "http://jsonplaceholder.typicode.com",
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, ""),
// },
// },
// },
}); });
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论