This commit is contained in:
Warunee Tamkoo 2024-12-16 23:13:43 +07:00
parent 50eecafad2
commit b8e1c415e1
18 changed files with 601 additions and 110 deletions

View file

@ -1,16 +1,173 @@
<script setup lang="ts">
import { onMounted } from "vue";
import { deleteCookie, getCookie, setCookie } from "@/plugins/cookie";
import router from "@/router";
import { computed, onMounted, ref } from "vue";
import { useQuasar } from "quasar";
import axios from "axios";
import CustomComponent from "@/components/CustomDialog.vue";
const $q = useQuasar();
const urlAdmin = import.meta.env.VITE_URL_ADMIN ?? "";
const urlUser = import.meta.env.VITE_URL_USER ?? "";
const urlMgt = import.meta.env.VITE_URL_MGT ?? "";
const urlCheckin = import.meta.env.VITE_URL_CHECKIN ?? "";
const token = ref<any>("");
const refreshToken = ref<any>("");
const fullname = computed(() => {
if (token.value) {
const base64Url = token.value.split(".")[1];
// Base64 URL-safe Base64
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
// Base64
const decoded = atob(base64);
const decodedData = JSON.parse(decoded);
//
return decodeURIComponent(escape(decodedData.name));
} else return "";
});
async function goPage(sys: string, url: string) {
// Payload JWT ( 2)
const base64Url = token.value.split(".")[1];
// Base64 URL-safe Base64
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
// Base64
const decoded = atob(base64);
// requiredRole sys
let requiredRole: string[] = [];
if (sys === "user" || sys === "checkin") {
requiredRole = ["USER"];
} else if (sys === "mgt") {
requiredRole = ["STAFF"]; // sys "ADMIN"
} else if (sys === "admin") {
requiredRole = ["ADMIN", "SUPER_ADMIN"];
}
console.log("requiredRole===>", requiredRole);
console.log("decoded===>", JSON.parse(decoded).realm_access.roles);
// payload.role role
if (
requiredRole.some((role) =>
JSON.parse(decoded).realm_access.roles.includes(role)
)
) {
window.location.href = `${url}/auth?token=${token.value}&accessToken=${refreshToken.value}`;
} else {
// alert("");
$q.dialog({
component: CustomComponent,
componentProps: {
title: `แจ้งเตือน`,
message: "คุณไม่มีสิทธิ์เข้าใช้งานระบบนี้",
icon: "warning",
color: "red",
onlycancel: true,
},
});
}
}
async function logout() {
await deleteCookie("BMAHRIS_KEYCLOAK_IDENTITY");
await deleteCookie("BMAHRIS_KEYCLOAK_REFRESH");
router.push("/sso");
}
onMounted(async () => {
// const checkAuthen = await authenticated();
// if (checkAuthen) {
// router.push("/");
// }
token.value = await getCookie("BMAHRIS_KEYCLOAK_IDENTITY");
refreshToken.value = await getCookie("BMAHRIS_KEYCLOAK_REFRESH");
deleteCookie("BMAHRISADM_KEYCLOAK_IDENTITY");
deleteCookie("BMAHRISCKI_KEYCLOAK_IDENTITY");
deleteCookie("BMAHRISUSER_KEYCLOAK_IDENTITY");
const checkToken = (await token.value) ?? null;
if (!checkToken && !token.value) {
await axios
.post(
`${import.meta.env.VITE_SSO_URL}/kcauth`,
{},
{
headers: {
"Content-Type": "application/json",
},
withCredentials: true, // Include cookies with the request
}
)
.then((res: any) => {
console.log("res===>", res);
if (res.status === 200) {
setCookie("BMAHRIS_KEYCLOAK_IDENTITY", res.data.access_token, 1);
setCookie("BMAHRIS_KEYCLOAK_REFRESH", res.data.refresh_token, 1);
}
})
.catch((err: any) => {
router.push("/sso");
});
}
});
</script>
<template>
<h1>Lanading page</h1>
<div class="q-ma-md">
<div class="row">
{{ fullname }}
<q-btn @click="logout()" class="btn_logout">
ออกจากระบบ <i class="mdi mdi-logout"></i>
</q-btn>
</div>
<div class="row">
<div class="col-4">
<img src="@/assets/screen1.png" style="width: 100%" />
<div class="h-100 py-3">
<a @click="goPage('user', urlUser)" class="link">
ระบบบรการเจาของขอม
</a>
</div>
</div>
<div class="col-4">
<img src="@/assets/screen2.png" style="width: 100%" />
<div class="h-100 py-3">
<a @click="goPage('checkin', urlCheckin)" class="link">
ระบบลงเวลาปฏราชการ
</a>
</div>
</div>
<div class="col-4">
<img src="@/assets/screen3.png" style="width: 100%" />
<div class="h-100 py-3">
<a @click="goPage('mgt', urlMgt)" class="link"> ระบบบรหารจดการ </a>
</div>
</div>
<div class="col-4">
<img src="@/assets/screen4.png" style="width: 100%" />
<div class="h-100 py-3">
<a @click="goPage('admin', urlAdmin)" class="link"> ระบบแอดม </a>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.link {
cursor: pointer;
}
</style>

298
src/views/sso.vue Normal file
View file

@ -0,0 +1,298 @@
<!-- authen with keycloak client -->
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useQuasar } from "quasar";
import axios from "axios";
import { useRouter } from "vue-router";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
const router = useRouter();
const mixin = useCounterMixin();
const { showLoader, hideLoader } = mixin;
const username = ref<string>(""); //
const password = ref<string>(""); //
const msgError = ref<string>(""); //
/**
* งกนเขาสระบบ
*/
async function onSubmit() {
showLoader();
msgError.value = "";
// const formdata = new URLSearchParams();
// formdata.append("username", username.value);
// formdata.append("password", password.value);
await axios
.post(
`${import.meta.env.VITE_SSO_URL}/signin`,
{
username: username.value,
password: password.value,
},
{
headers: {
"Content-Type": "application/json",
},
withCredentials: true, // Include cookies with the request
}
)
.then((res) => {
if (res.data === "OK") {
router.push("/");
}
console.log("res===>", res);
})
.catch((err) => {
msgError.value = "ชื่อผู้ใช้งานหรือรหัสผ่านไม่ถูกต้อง";
})
.finally(() => {
hideLoader();
});
}
/**
* ทำงานเม components กเรยกใชงาน
*/
onMounted(async () => {});
</script>
<template>
<div class="bg-image">
<div class="shadow-top"></div>
<div class="shadow-bottom"></div>
<div class="row fit items-center">
<div class="row full-width justify-evenly">
<div class="">
<div id="myimage"></div>
<div class="text-logo">
ระบบบรหารจดการการใชงาน <br />ระบบสารสนเทศสนบสนนการเชอมโยง
</div>
<div class="subtext-logo">
Bangkok Metropolitan Administration Single Sign-On
</div>
</div>
<img src="@/assets/line.png" class="img_absolute_line" />
<div class="card-pf">
<div class="row q-gutter-sm items-center q-mb-sm">
<div class="column">
<div class="login-pf-header">
<h1 id="kc-page-title">เขาใชงานระบบ</h1>
</div>
</div>
</div>
<q-form
greedy
@submit.prevent
@validation-success="onSubmit"
style="max-width: 100%; min-width: 30%"
>
<div class="row">
<div class="col-12 form-group">
<label
for="username"
class="pf-c-form__label pf-c-form__label-text"
>อผใชงาน</label
>
<q-input
v-model="username"
dense
outlined
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อผู้ใช้งาน'}`,]"
></q-input>
</div>
<div class="col-12 form-group">
<label
for="username"
class="pf-c-form__label pf-c-form__label-text"
>รหสผาน</label
>
<q-input
v-model="password"
dense
outlined
lazy-rules
hide-bottom-space
type="password"
:rules="[(val:string) => !!val || `${'กรุณากรอกรหัสผ่าน'}`,]"
></q-input>
</div>
<div class="col-12 text-red text-center">{{ msgError }}</div>
<div class="col-12">
<q-btn
unelevated
color="primary"
class="full-width"
label="เข้าระบบ"
style="font-size: 16px; border-radius: 8px"
type="submit"
>
</q-btn>
</div>
</div>
</q-form>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.shadow-top {
position: absolute;
height: 4rem;
width: 4rem;
border-radius: 50%;
background-color: $primary;
top: -4rem;
right: -4rem;
opacity: 0.5;
box-shadow: 0px 0px 300px 320px $primary;
backdrop-filter: blur(550px);
}
.shadow-bottom {
position: absolute;
height: 4rem;
width: 4rem;
border-radius: 50%;
background-color: $primary;
bottom: -4rem;
left: -4rem;
opacity: 0.5;
box-shadow: 0px 0px 300px 320px $primary;
backdrop-filter: blur(550px);
}
.color-icon {
border-radius: 8px;
}
.link {
text-decoration: none;
color: #cc0004;
}
.link:hover {
color: #ff0004;
text-decoration: underline;
}
.checkbox,
.radio {
position: relative;
display: block;
margin-top: 10px;
margin-bottom: 10px;
}
input[type="checkbox"],
input[type="radio"] {
margin: 1px 3px 0 0;
line-height: normal;
}
.checkbox label,
.radio label {
min-height: 20px;
margin-bottom: 0;
font-weight: 400;
cursor: pointer;
}
.form-group {
margin-bottom: 15px;
text-align: left;
}
#kc-content {
width: 100%;
}
#kc-content-wrapper {
margin-top: 20px;
}
#kc-form-options .checkbox {
margin-top: 0;
color: #72767b;
}
.bg-image {
font-family: "Noto Sans Thai", sans-serif;
font-family: "Noto Sans Thai", sans-serif;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-size: cover;
height: 100vh;
background: rgb(30, 50, 49);
background: linear-gradient(
0deg,
rgba(30, 50, 49, 1) 0%,
rgba(20, 120, 99, 1) 100%
);
}
#myimage {
width: 120px;
height: 120px;
object-position: center;
background-repeat: no-repeat;
background-size: 100% !important;
background: url(@/assets/logo.png) no-repeat center center;
margin-bottom: 20px;
}
.text-logo {
text-align: left;
color: white;
font-weight: 600;
font-size: 1.4rem;
}
.subtext-logo {
color: #fff;
font-weight: 200;
font-size: 1rem;
}
.card-pf {
padding: 20px;
max-width: 400px;
min-width: 30%;
position: relative;
z-index: 10 !important;
border-radius: 10px;
background: #fff;
}
.login-pf-page .login-pf-header {
display: -ms-flexbox;
display: flex;
-ms-flex-direction: column;
flex-direction: column;
}
.login-pf-page .login-pf-header h1 {
flex-wrap: wrap;
display: flex;
justify-content: start;
}
h1#kc-page-title {
font-weight: 800;
font-size: 1.3rem;
line-height: 1.1;
}
.login-pf-page .login-pf-header h1 {
font-size: 16px;
}
.img_absolute_line {
position: absolute;
height: auto;
width: 400px;
left: 0;
bottom: 0;
}
</style>