vue基于session和github-oauth2實(shí)現(xiàn)登錄注冊(cè)驗(yàn)證思路詳解
本文簡(jiǎn)介
本文講解前端通過簡(jiǎn)單的方式實(shí)現(xiàn)一個(gè)基本的登錄注冊(cè)驗(yàn)證功能。在后端,使用 session 表來存儲(chǔ)會(huì)話信息,并讓前端的 localStorage
存儲(chǔ) sessionId
。通過 sessionId
可以在 session 表中獲取用戶的信息。此外,還利用 session 表實(shí)現(xiàn)了 GitHub 的 OAuth2 第三方登錄。
基本思路
在本項(xiàng)目中,我們使用 Vue 作為前端框架,通過與后端 API 交互來管理用戶的會(huì)話狀態(tài)。
- 用戶注冊(cè)和登錄:用戶在前端輸入用戶名和密碼,通過表單提交到后端進(jìn)行驗(yàn)證。成功登錄后,后端返回一個(gè)
sessionId
,前端將其存儲(chǔ)在localStorage
中。 - 會(huì)話管理:前端在每次請(qǐng)求時(shí)會(huì)在請(qǐng)求頭中附帶
sessionId
,以便后端能夠驗(yàn)證用戶的身份。 - 第三方登錄:用戶點(diǎn)擊
GitHub
登錄按鈕后,前端會(huì)重定向到GitHub
的授權(quán)頁(yè)面。用戶授權(quán)后,GitHub
重定向回我們的網(wǎng)站,并附帶授權(quán)碼。前端將授權(quán)碼發(fā)送到后端服務(wù)器以換取訪問令牌。
框架版本
- Vue 3
- Pinia (用于狀態(tài)管理)
- Vue Router (用于路由管理)
- Axios (用于 HTTP 請(qǐng)求)
- tailwind (用于css)
- element-plus (用于css)
開源地址
后端地址:springboot-session-github-oauth
前端地址:vite-session-github-oauth
主要學(xué)習(xí)點(diǎn)
路由守衛(wèi)
路由守衛(wèi)確保只有經(jīng)過身份驗(yàn)證的用戶才能訪問某些頁(yè)面。
// 添加全局前置守衛(wèi) router.beforeEach(async (to, from, next) => { if (to.name == "Home") { const sessionId = ref(""); const sessionIdFromUrl = to.query.sessionId as string; if (sessionIdFromUrl) { sessionId.value = sessionIdFromUrl; localStorage.setItem('sessionId', sessionIdFromUrl); } else if (localStorage.getItem('sessionId')) { sessionId.value = localStorage.getItem('sessionId') || ''; } if (sessionId.value == '') { // 沒有 sessionId,重定向到登錄頁(yè)面 return next({ name: 'Login' }); } else { try { // 驗(yàn)證 sessionId 是否有效 const response = await axios.get('http://localhost:8080/auth/home', { params: { sessionId: sessionId.value } }); if (response.data && response.data.data) { // 如果 username 存在,則 sessionId 有效 localStorage.setItem('username', response.data.data); next(); } else { // 如果無效,清除 localStorage 并重定向到登錄頁(yè)面 localStorage.removeItem('sessionId'); next({ name: 'Login' }); } } catch (error) { console.error('驗(yàn)證 sessionId 失敗:', error); localStorage.removeItem('sessionId'); next({ name: 'Login' }); } } } else { // 如果目標(biāo)路由是登錄頁(yè)面,直接繼續(xù)導(dǎo)航 next(); } });
代碼設(shè)計(jì)
Login.vue
<script setup lang="ts"> import { ref } from 'vue'; import axios from 'axios'; import router from '../routes.ts'; const username = ref(''); const password = ref(''); const handleLogin = async () => { try { const response = await axios.post('http://localhost:8080/auth/login',{ username: username.value, password: password.value }); localStorage.setItem('sessionId', response.data.data); console.log(response.data.data); await router.push('/home'); } catch (error) { console.log(error); } } const handleLoginByGithub = () => { window.location.href = 'http://localhost:8080/oauth/authorize'; } </script> <template> <div class="flex justify-center items-center min-h-screen bg-gray-2"> <div class="w-full max-w-md p-8 space-y-8 bg-white rounded-lg shadow-lg"> <h2 class="text-2xl font-bold text-center text-gray-800"> 客戶端登錄(session登錄) </h2> <form class="space-y-6" @submit.prevent> <div> <label for="username" class="block text-sm font-medium text-gray-700"> 用戶名 </label> <input v-model="username" id="username" name="username" type="text" required class="w-full px-3 py-2 mt-1 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500" /> </div> <div> <label for="password" class="block text-sm font-medium text-gray-700"> 密碼 </label> <input v-model="password" id="password" name="password" type="password" required class="w-full px-3 py-2 mt-1 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500" /> </div> <div class="flex justify-end"> <router-link to="/register"> 還沒有賬號(hào),點(diǎn)擊注冊(cè) </router-link> </div> <div> <button type="submit" class="w-full px-4 py-2 font-semibold text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" @click="handleLoginByGithub"> 使用GiHub登錄 </button> </div> <div> <button type="submit" class="w-full px-4 py-2 font-semibold text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" @click="handleLogin"> 登錄 </button> </div> </form> </div> </div> </template> <style scoped></style>
Register.vue
<script setup lang="ts"> import { ref } from 'vue'; import axios from 'axios'; import router from '../routes'; const username = ref(''); const password = ref(''); const handleRegister = async () => { try { const response = await axios.post('http://localhost:8080/auth/register', { username: username.value, password: password.value, }); console.log(response.data); localStorage.setItem('sessionId', response.data.data); await router.push('/home'); } catch (error) { console.log(error); } }; </script> <template> <div class="flex justify-center items-center min-h-screen bg-gray-100"> <div class="w-full max-w-md p-8 space-y-8 bg-white rounded-lg shadow-lg"> <h2 class="text-2xl font-bold text-center text-gray-800"> 客戶端注冊(cè)(session注冊(cè)) </h2> <form class="space-y-6" @submit.prevent="handleRegister"> <div> <label for="username" class="block text-sm font-medium text-gray-700"> 用戶名 </label> <input v-model="username" id="username" name="username" type="text" required class="w-full px-3 py-2 mt-1 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500" /> </div> <div> <label for="password" class="block text-sm font-medium text-gray-700"> 密碼 </label> <input v-model="password" id="password" name="password" type="password" required class="w-full px-3 py-2 mt-1 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500" /> </div> <div class="flex justify-end"> <router-link to="/login"> 已有賬號(hào)?點(diǎn)擊登錄 </router-link> </div> <div> <button type="submit" class="w-full px-4 py-2 font-semibold text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" @click.prevent="handleRegister"> 注冊(cè) </button> </div> </form> </div> </div> </template> <style scoped></style>
Home.vue
<script setup lang="ts"> import { onMounted, ref } from 'vue'; import axios from 'axios'; import router from '../routes'; import { ElMessage } from 'element-plus'; const username = ref(''); const sessionId = ref(''); const state = ref(''); const showPasswordDialog = ref(false); const newPassword = ref(''); const confirmPassword = ref(''); // New ref for confirming password username.value = localStorage.getItem('username'); sessionId.value = localStorage.getItem('sessionId'); console.log(sessionId.value); const handleState = async () => { try { const response = await axios.get('http://localhost:8080/user/state', { params: { sessionId: sessionId.value } }); state.value = response.data.data; } catch (error) { console.log(error); } }; const handleAddPassword = async () => { if (newPassword.value !== confirmPassword.value) { ElMessage.error('Passwords do not match'); return; } try { const response = await axios.post('http://localhost:8080/user/addPassword', { password: newPassword.value }, { params: { sessionId: sessionId.value, } }); console.log(response.data); if (response.data.status === 'OK') { ElMessage.success('Password added successfully'); showPasswordDialog.value = false; newPassword.value = ''; confirmPassword.value = ''; // Reset password fields } else { ElMessage.error('Failed to add password'); } } catch (error) { console.log(error); ElMessage.error('An error occurred while adding the password'); } }; const handleLoginByGitHub = async () => { window.location.href = 'http://localhost:8082/oauth/authorize?sessionId=' + sessionId.value; } const handleLogout = async () => { try { const response = await axios.get('http://localhost:8080/auth/logout', { params: { sessionId: sessionId.value } }); localStorage.removeItem('sessionId'); sessionId.value = ''; await router.push('/login'); console.log(response.data); } catch (error) { console.log(error); } }; // Execute on component mount onMounted(() => { handleState(); }); </script> <template> <div class="flex justify-center items-center min-h-screen bg-gray-2"> <div class="w-full max-w-md p-8 space-y-8 bg-white rounded-lg shadow-lg"> <h2 class="text-2xl font-bold text-center text-gray-800"> 使用Session登錄成功 </h2> <p class="text-center text-gray-600"> 歡迎回來, {{ username }} </p> <div v-if="state == 'password'" class="flex justify-end"> <button class="px-4 py-2 font-semibold text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" @click="showPasswordDialog = true"> 添加密碼 </button> </div> <div v-else-if="state == 'githubLoginName'" class="flex justify-end"> <button class="px-4 py-2 font-semibold text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" @click="handleLoginByGitHub"> 綁定github賬號(hào) </button> </div> <div class="flex justify-end"> <button class="px-4 py-2 font-semibold text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" @click="handleLogout"> 退出 </button> </div> </div> </div> <!-- Password Dialog --> <el-dialog v-model="showPasswordDialog" title="添加密碼" width="500" draggable> <div class="space-y-4"> <!-- New Password --> <div class="flex items-center space-x-4"> <label for="newPassword" class="text-sm font-medium text-gray-700"> 輸入密碼 </label> <input v-model="newPassword" id="newPassword" name="newPassword" type="password" required class="w-5/6 px-4 py-2 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500" /> </div> <!-- Confirm Password --> <div class="flex items-center space-x-4"> <label for="confirmPassword" class="text-sm font-medium text-gray-700"> 確認(rèn)密碼 </label> <input v-model="confirmPassword" id="confirmPassword" name="confirmPassword" type="password" required class="w-5/6 px-4 py-2 border border-gray-300 rounded-lg focus:ring-indigo-500 focus:border-indigo-500" /> </div> </div> <template #footer> <div class="dialog-footer"> <el-button @click="showPasswordDialog = false">取消</el-button> <el-button type="primary" @click="handleAddPassword">確定</el-button> </div> </template> </el-dialog> </template> <style scoped></style>
到此這篇關(guān)于vue基于session和github-oauth2實(shí)現(xiàn)登錄注冊(cè)驗(yàn)證的文章就介紹到這了,更多相關(guān)vue登錄注冊(cè)驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-cli如何關(guān)閉Uncaught?error的全屏提示
這篇文章主要介紹了vue-cli如何關(guān)閉Uncaught?error的全屏提示問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04Vue實(shí)現(xiàn)將數(shù)據(jù)庫(kù)中帶html標(biāo)簽的內(nèi)容輸出(原始HTML(Raw HTML))
今天小編就為大家分享一篇Vue實(shí)現(xiàn)將數(shù)據(jù)庫(kù)中帶html標(biāo)簽的內(nèi)容輸出(原始HTML(Raw HTML)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-10-10使用Vue3優(yōu)雅地實(shí)現(xiàn)表格拖動(dòng)排序
在?Vue.js?中主要通過第三方庫(kù)實(shí)現(xiàn)表格拖動(dòng)排序功能,其中最常用的庫(kù)是?SortableJS,下面我們就來看看如何使用SortableJS實(shí)現(xiàn)表格拖動(dòng)排序吧2025-01-01