Vue 3 版本的 JWT 单点登录 (SSO) 实现
单点认证体系(SSO)作为现代身份管理的重要组成部分,通过统一的认证机制实现了跨系统访问的无缝衔接。以下从用户终端视角剖析该系统的运行机制。
- 系统架构组成
单点认证体系主要包含三大核心模块:
认证服务中心:作为整个体系的核心枢纽,承担用户身份核验、凭证签发和会话管理的职责。该服务通常采用集中式部署,为所有关联应用提供标准化的认证接口。
接入应用系统:指需要用户身份验证的各类业务系统。这些系统通过标准化协议与认证中心对接,在用户访问时自动完成认证流程。
用户终端环境:作为认证流程的起点和交互界面,通常指用户使用的浏览器或移动应用。终端负责维护认证状态,并在各应用间传递认证凭证。
这种架构设计通过将认证逻辑从业务系统中剥离,实现了"一次认证,全网通行"的效果。各业务系统无需单独维护用户凭证,只需信任认证中心颁发的令牌即可完成用户识别。
- 前端应用入口 (Vue 3 版本)
<template><div><div v-if="isLoading">加载中...</div><div v-else-if="!isAuthenticated">正在重定向到登录页面...</div><div v-else><header><p>欢迎, {{ user.name }}</p><button @click="handleLogout">退出登录</button></header><main><!-- 应用内容 --></main></div></div>
</template><script>
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';export default {name: 'TokenBasedApp',setup() {const isAuthenticated = ref(false);const user = ref(null);const isLoading = ref(true);const router = useRouter();const redirectToSSOLogin = () => {const appId = 'app1';const callbackUrl = encodeURIComponent(window.location.origin);window.location.href = `https://sso.example.com/login?appId=${appId}&callback=${callbackUrl}`;};const validateToken = async () => {const token = localStorage.getItem('auth_token');if (!token) {isLoading.value = false;redirectToSSOLogin();return;}try {const response = await fetch('https://app1.example.com/api/auth/validate', {headers: {'Authorization': `Bearer ${token}`}});if (response.ok) {const userData = await response.json();isAuthenticated.value = true;user.value = userData;isLoading.value = false;} else {localStorage.removeItem('auth_token');isLoading.value = false;redirectToSSOLogin();}} catch (error) {console.error('Token验证失败:', error);isLoading.value = false;redirectToSSOLogin();}};const handleLogout = () => {localStorage.removeItem('auth_token');const callbackUrl = encodeURIComponent(window.location.origin);window.location.href = `https://sso.example.com/logout?callback=${callbackUrl}`;};onMounted(() => {const urlParams = new URLSearchParams(window.location.search);const token = urlParams.get('token');if (token) {localStorage.setItem('auth_token', token);window.history.replaceState({}, document.title, window.location.pathname);}validateToken();});return {isAuthenticated,user,isLoading,handleLogout};}
};
</script>
- JWT 登录页面 (Vue 3 版本)
<template><div class="login-container"><h2>统一登录平台</h2><div v-if="error" class="error-message">{{ error }}</div><form @submit.prevent="handleSubmit"><div class="form-group"><label for="username">用户名</label><inputtype="text"id="username"v-model="username"required/></div><div class="form-group"><label for="password">密码</label><inputtype="password"id="password"v-model="password"required/></div><button type="submit" :disabled="isLoading">{{ isLoading ? '登录中...' : '登录' }}</button></form></div>
</template><script>
import { ref } from 'vue';
import { useRoute } from 'vue-router';export default {name: 'JWTLoginPage',setup() {const username = ref('');const password = ref('');const error = ref(null);const isLoading = ref(false);const route = useRoute();const handleSubmit = async () => {isLoading.value = true;error.value = null;try {const appId = route.query.appId;const callbackUrl = route.query.callback;if (!appId || !callbackUrl) {throw new Error('缺少必要的参数');}const response = await fetch('https://sso.example.com/api/token', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ username: username.value, password: password.value,appId})});if (!response.ok) {const errorData = await response.json();throw new Error(errorData.message || '登录失败');}const { token } = await response.json();window.location.href = `${decodeURIComponent(callbackUrl)}?token=${token}`;} catch (err) {error.value = err.message;isLoading.value = false;}};return {username,password,error,isLoading,handleSubmit};}
};
</script><style scoped>
.login-container {max-width: 400px;margin: 0 auto;padding: 20px;
}.form-group {margin-bottom: 15px;
}label {display: block;margin-bottom: 5px;
}input {width: 100%;padding: 8px;box-sizing: border-box;
}button {padding: 10px 15px;background-color: #42b983;color: white;border: none;cursor: pointer;
}button:disabled {background-color: #cccccc;cursor: not-allowed;
}.error-message {color: red;margin-bottom: 15px;
}
</style>
单点登录sso的实现可以归纳为如下:
- 所有客户端的登录默认都走一个页面
- 前端应用入口:进入业务页面时,
① 先校验是否携带的token,如果没有则直接携带appid和当前url跳转登录页。
②如果携带token,调接口查询token是否正常,正常则获取用户信息
③token不正常,携带appid和当前url跳转登录页 - JWT 登录页面 :页面获取从业务页面携带的appid和callbackUrl,使用统一登录,成功后,通过callbackUrl和token,跳转回原业务页面,继续走获取用户信息的方法。