vue基于session和github-oauth2實現(xiàn)登錄注冊驗證思路詳解
本文簡介
本文講解前端通過簡單的方式實現(xiàn)一個基本的登錄注冊驗證功能。在后端,使用 session 表來存儲會話信息,并讓前端的 localStorage 存儲 sessionId。通過 sessionId 可以在 session 表中獲取用戶的信息。此外,還利用 session 表實現(xiàn)了 GitHub 的 OAuth2 第三方登錄。
基本思路
在本項目中,我們使用 Vue 作為前端框架,通過與后端 API 交互來管理用戶的會話狀態(tài)。
- 用戶注冊和登錄:用戶在前端輸入用戶名和密碼,通過表單提交到后端進(jìn)行驗證。成功登錄后,后端返回一個
sessionId,前端將其存儲在localStorage中。 - 會話管理:前端在每次請求時會在請求頭中附帶
sessionId,以便后端能夠驗證用戶的身份。 - 第三方登錄:用戶點擊
GitHub登錄按鈕后,前端會重定向到GitHub的授權(quán)頁面。用戶授權(quán)后,GitHub重定向回我們的網(wǎng)站,并附帶授權(quán)碼。前端將授權(quán)碼發(fā)送到后端服務(wù)器以換取訪問令牌。
框架版本
- Vue 3
- Pinia (用于狀態(tài)管理)
- Vue Router (用于路由管理)
- Axios (用于 HTTP 請求)
- tailwind (用于css)
- element-plus (用于css)
開源地址
后端地址:springboot-session-github-oauth
前端地址:vite-session-github-oauth
主要學(xué)習(xí)點
路由守衛(wèi)
路由守衛(wèi)確保只有經(jīng)過身份驗證的用戶才能訪問某些頁面。
// 添加全局前置守衛(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,重定向到登錄頁面
return next({ name: 'Login' });
} else {
try {
// 驗證 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 并重定向到登錄頁面
localStorage.removeItem('sessionId');
next({ name: 'Login' });
}
} catch (error) {
console.error('驗證 sessionId 失敗:', error);
localStorage.removeItem('sessionId');
next({ name: 'Login' });
}
}
} else {
// 如果目標(biāo)路由是登錄頁面,直接繼續(xù)導(dǎo)航
next();
}
});代碼設(shè)計
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">
還沒有賬號,點擊注冊
</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">
客戶端注冊(session注冊)
</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">
已有賬號?點擊登錄
</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">
注冊
</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賬號
</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實現(xiàn)登錄注冊驗證的文章就介紹到這了,更多相關(guān)vue登錄注冊驗證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-cli如何關(guān)閉Uncaught?error的全屏提示
這篇文章主要介紹了vue-cli如何關(guān)閉Uncaught?error的全屏提示問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04
Vue實現(xiàn)將數(shù)據(jù)庫中帶html標(biāo)簽的內(nèi)容輸出(原始HTML(Raw HTML))
今天小編就為大家分享一篇Vue實現(xiàn)將數(shù)據(jù)庫中帶html標(biāo)簽的內(nèi)容輸出(原始HTML(Raw HTML)),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-10-10

