Files
Nilai_Clock/src/views/LoginView.vue
T
sudomarcma e563f17283 feat(安全): 实现JWT认证和HTTPS支持
- 添加JWT认证中间件保护API端点
- 在登录流程中使用bcrypt哈希密码和JWT令牌
- 配置HTTPS服务器使用自签名证书
- 更新前端API调用以包含认证令牌
2025-06-26 10:41:23 +08:00

118 lines
2.9 KiB
Vue

<template>
<div class="login-wrapper">
<div class="login-card card">
<h2>Login</h2>
<form @submit.prevent="handleLogin">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" v-model="username" class="form-input" required />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" v-model="password" class="form-input" required />
</div>
<p class="info">Hint: worker/password or manager/password</p>
<button type="submit" class="button-primary login-button" :disabled="loading">
{{ loading ? 'Logging in...' : 'Login' }}
</button>
<p v-if="error" class="error-message">{{ error }}</p>
</form>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const username = ref('')
const password = ref('')
const error = ref('')
const loading = ref(false)
const handleLogin = async () => {
loading.value = true
error.value = ''
try {
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: username.value, password: password.value }),
})
const data = await response.json()
if (response.ok) {
// Store token and user info in session storage
sessionStorage.setItem('token', data.token)
try {
const decodedToken = JSON.parse(atob(data.token.split('.')[1]))
sessionStorage.setItem('userId', decodedToken.userId)
sessionStorage.setItem('userRole', decodedToken.role)
if (decodedToken.role === 'worker') {
router.push('/worker/dashboard')
} else if (decodedToken.role === 'manager') {
router.push('/manager/dashboard')
}
} catch (e) {
error.value = 'Invalid token received from server.'
}
} else {
error.value = data.message
}
} catch {
error.value = 'Failed to connect to the server.'
} finally {
loading.value = false
}
}
</script>
<style scoped>
/* Styles remain the same */
.login-wrapper {
display: flex;
justify-content: center;
align-items: center;
min-height: 70vh;
}
.login-card {
width: 100%;
max-width: 400px;
}
.login-card h2 {
text-align: center;
margin-top: 0;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-input {
width: 100%;
box-sizing: border-box;
}
.info {
font-size: 0.8rem;
color: var(--c-text-secondary);
text-align: center;
margin: -0.5rem 0 1.5rem 0;
}
.login-button {
width: 100%;
padding: 12px;
font-size: 1.1rem;
}
.error-message {
color: var(--c-danger);
text-align: center;
margin-top: 1rem;
}
</style>