fix: fix login bug

v3
veypi 3 months ago
parent cbf9f7a6e6
commit 134ab38751

@ -65,7 +65,7 @@ func userLogin(x *vigo.X) (any, error) {
}
err = query.First(user).Error
if err != nil {
return nil, err
return nil, vigo.ErrNotFound
}
logv.Info().Str("user", user.ID).Msg("login")
code, err := base64.URLEncoding.DecodeString(opts.Code)

@ -2,7 +2,6 @@ import routes from './routes.js'
import token from './token.js'
export default ($env) => {
console.log($env, $vyes.root)
token.setBaseUrl($env.root)
token.wrapAxios($env.$axios)
$env.$G.token = token
@ -27,10 +26,10 @@ export default ($env) => {
}
};
$env.$axios.interceptors.response.use(function(response) {
console.log(response)
return response?.data || response;
}, function(error) {
console.error('Axios Error:', error);
error = error?.response?.data || error?.response || error
return Promise.reject(error.message || error);
return Promise.reject(error);
});
}

@ -377,7 +377,7 @@
const response = await $axios.post('/api/user', {
username: signUpForm.username,
code: btoa(signUpForm.password),
});
}, {noretry: true});
if (response) {
alert('注册成功!');
switchToSignIn();
@ -395,13 +395,17 @@
const loginResponse = await $axios.post('/api/user/login', {
username: signInForm.username,
code: btoa(signInForm.password),
});
if (loginResponse) {
}, {noretry: true});
if (loginResponse && typeof loginResponse === 'string') {
localStorage.setItem('refresh', loginResponse)
window.location.href = redirect
} else {
console.warn('登录失败,服务器返回异常数据', loginResponse);
$message.warning('服务器异常');
}
} catch (error) {
alert(error.message || '登录失败,请检查您的凭据。');
console.warn(error.message || error)
$message.warning('登录失败,请检查您的凭据。');
}
};

@ -1,396 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Login</title>
</head>
<style>
.login-page {
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
background-color: #fafafa;
background-image: url("../assets/img/bg.svg");
background-size: cover;
background-position: center;
}
.auth-line {
display: flex;
gap: 1rem;
}
.box {
user-select: none;
position: sticky;
padding: 2rem;
width: 50%;
max-width: 50rem;
min-width: 20rem;
height: 50%;
}
.box::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 2rem;
background-color: rgba(200, 200, 200, 0.2);
backdrop-filter: blur(20px);
z-index: -1;
}
.header {
line-height: 2rem;
width: 100%;
height: 4rem;
}
.voa-logo {
height: 4rem;
width: 4rem;
background-image: url("../assets/img/favicon.svg");
background-size: cover;
background-position: center;
}
.txt {
font-size: 1.5rem;
}
.content {
display: flex;
flex-direction: column;
justify-content: space-between;
height: calc(100% - 4rem);
}
.username,
.password {
position: relative;
width: 100%;
}
.username input,
.password input {
height: 2.5rem;
line-height: 2.5rem;
font-size: 1.5rem;
width: calc(100% - 2rem);
margin: 0 1rem;
border: none;
outline: none;
background: none;
}
.username::after,
.password::after {
content: "";
position: absolute;
bottom: 0;
left: 1rem;
width: calc(100% - 2rem);
height: 0.1em;
background-color: #000;
transition: all 0.3s;
}
.username:hover::after,
.password:hover::after {
left: 0%;
width: 100%;
background-color: #00ffff;
}
.username[check='false']::after,
.password[check='false']::after {
background-color: #f00 !important;
}
.ok {
line-height: 3rem;
font-size: 1.5rem;
height: 3rem;
margin: 0 auto;
width: 40%;
background: #73f7ca;
border-radius: 1.5rem;
}
.back {
background: #ccc;
opacity: 0.5;
}
.back:hover {
opacity: 1;
}
</style>
<body>
<div class="login-page flex items-center justify-center">
<div class="login box">
<div class="header flex items-center justify-start gap-2">
<div class="voa-logo"></div>
<div class="txt">OneAuth</div>
</div>
<div class="newbie content" v-if="aOpt === 'newbie'">
<div :check="checks.u" class="username mt-8">
<input @input="check" !value="data.username" autocomplete="username" placeholder="username, phone or Email">
</div>
<div :check="checks.p" class="password">
<input @input="check" !value="data.password" autocomplete="password" type="password" placeholder="password">
</div>
<div :check="checks.p2" class="password">
<input @input="check" !value="data.confirm" autocomplete="password" type="password"
placeholder="confirm password">
</div>
<div class="flex">
<button @click="aOpt = ''" class="ok voa-btn back">Back</button>
<button @click="register" class="ok voa-btn">Register</button>
</div>
</div>
<div class="oh_no content" v-else-if="aOpt === 'oh_no'">
<div class="username mt-8">
<input !value="data.username" autocomplete="username" placeholder="username, phone or Email">
</div>
<div class="flex">
<button @click="aOpt = ''" class="ok back voa-btn">Back</button>
<button @click="reset" class="ok voa-btn">Confirm</button>
</div>
</div>
<div class="login content" v-else-if="isValid">
<div class="flex mt-10 h-full" v-if="app.id">
<div class="flex flex-col items-center w-1/2 justify-center">
<img class="rounded-full h-44 w-44" :src="oaer.local().icon">
<div class="mt-4 text-2xl">{{ oaer.local().nickname || oaer.local().username }}</div>
</div>
<div class="flex flex-col w-1/2 gap-4">
<div class="flex items-center justify-start gap-4">
<img class="rounded-full h-16 w-16" :src="app.icon">
<div>{{ app.name }}</div>
</div>
<div class="flex-grow">
<div>您正在授权登录 <span class="font-bold text-xl">{{ app.name }}</span></div>
<div class="mt-8 ml-8 flex flex-col gap-4">
<div class="auth-line">
<UToggle color="primary" :model-value="true" disabled />
<div class="auth-info">Basic User Info</div>
</div>
<div class="auth-line">
<UToggle color="primary" v:model="app_perm.fs[0]" />
<div class="auth-info flex">
<UInput v-if="app_perm.fs[0]" :padded="false" v:model="app_perm.fs[1]"
placeholder="userfile auth scope" variant="none" class="w-full border-b-black border-b-2" />
<span v-else>Userfile Permission</span>
</div>
</div>
</div>
</div>
<div class="flex">
<button @click="signout" class="ok back voa-btn">Sign Out</button>
<button @click="redirect()" class="ok voa-btn">Sign In</button>
</div>
<div class="text-sm text-gray-600 text-center">
Authorizing will redirect to {{ app.init_url }}
</div>
</div>
</div>
<div class="flex mt-10 h-full justify-center items-center flex-col" v-else>
<img class="rounded-full h-44 w-44" :src="oaer.local().icon">
<div class="mt-4 text-2xl">{{ oaer.local().nickname || oaer.local().username }}</div>
<div class="flex-grow"></div>
<div class="flex w-1/2">
<button @click="signout" class="ok back voa-btn">Sign Out</button>
<button @click="redirect()" class="ok voa-btn">Sign In</button>
</div>
</div>
</div>
<div class="login content flex flex-col justify-between" v-else>
<div :check="checks.u" class="username mt-8">
<input @input="check" !value="data.username" autocomplete="username" placeholder="username, phone or Email">
</div>
<div :check="checks.p" class="password">
<input @input="check" !value="data.password" autocomplete="password" type="password" placeholder="password">
</div>
<button @click="signin" class="ok voa-btn">Sign In</button>
<div class="last">
<div class="icos">
<div class="github"></div>
<div class="wechat"></div>
<div class="google"></div>
</div>
<div class="">
<div @click="aOpt = 'newbie'">Create Account</div>
<div @click="aOpt = 'oh_no'">Forgot Password?</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script setup>
aOpt = ''
isValid = false
uuid = ''
checks = {u: true, p: true, p2: true}
app = {}
app_perm = {fs: [true, '/', 4], app: [true, '', 1], user: [true, '', 1]}
data = {
username: '',
password: '',
confirm: '',
}
const auto_redirect = () => {
if (isValid) {
if (uuid) {
api.app.Get(uuid).then(e => {
app = e
console.log(oaer.local())
api.token.List({limit: 1, app_id: uuid, user_id: oaer.local().id}).then(e => {
console.log(e)
})
}).catch(e => {
if (e.code === 40401) {
msg.Warn('参数错误: 该应用不存在')
uuid = ''
redirect()
return
}
console.warn(e)
})
} else {
redirect()
}
}
}
let uReg = /^[\w]{5,}$/
let pReg = /^[\w@_#]{6,}$/
let enable_check = false
const check = () => {
if (!enable_check) return
checks.u = Boolean(data.username && uReg.test(data.username))
checks.p = Boolean(data.password && pReg.test(data.password))
checks.p2 = Boolean(data.confirm === data.password)
}
const signout = () => {
oaer.logout()
isValid = false
}
const signin = () => {
enable_check = true
check()
if (!checks.u || !checks.p) return
api.token.TokenSalt({username: data.username}).then(e => {
let id = e.id
let key = deriveKey(data.password, e.salt)
let salt = crypto.lib.WordArray.random(128 / 8)
let opts = {
iv: salt,
mode: crypto.mode.CBC,
padding: crypto.pad.Pkcs7
}
let p = crypto.AES.encrypt(e.id, key, opts)
api.token.Post({
user_id: id,
code: p.toString(),
salt: salt.toString()
}).then(e => {
oaer.init('', '', e).then(() => {
isValid = true
auto_redirect()
}).catch((e) => {
console.warn(e)
msg.Warn('登录失败:' + (e?.err || e))
})
}).catch(e => {
msg.Warn('登录失败:' + (e?.err || e))
})
}).catch(e => {
if (e.code === 40401) {
msg.Warn('用户不存在')
} else {
console.warn(e)
}
})
}
const register = () => {
enable_check = true
check()
if (!checks.u || !checks.p || !checks.p2) return
let salt = crypto.lib.WordArray.random(128 / 8).toString()
let key = deriveKey(data.password, salt)
api.user.Post({
username: data.username,
salt: salt,
code: key.toString(crypto.enc.Hex)
}).then(() => {
msg.Info('注册成功')
aOpt = ''
}).catch(e => {
console.log(e)
msg.Warn('注册失败:' + (e.err || e))
})
}
const reset = () => {
enable_check = true
check()
}
const redirect = (url) => {
if (url === 'undefined') url = ''
if (route.query.redirect) url = route.query.redirect
if (uuid) {
api.app.Get(uuid).then((app) => {
if (uuid === oaer.logic().oa_id) {
oaer.goto(url || app.init_url || '/')
} else {
let perm = []
for (let i in app_perm) {
let p = app_perm[i]
if (p[0]) {
perm.push({
name: i,
tid: p[1],
level: p[2]
})
}
}
api.token.Post({
refresh: oaer.logic().token.refresh.raw(),
app_id: uuid,
over_perm: JSON.stringify(perm)
}).then(e => {
url = url || app.init_url
e = encodeURIComponent(e)
if (url.indexOf('$token') >= 0) {
url = url.replaceAll('$token', e)
}
oaer.goto(url, {token: e})
})
}
})
} else if (url) {
oaer.goto(url)
} else {
oaer.goto('/')
}
}
</script>
</html>

@ -69,7 +69,7 @@ class TokenService {
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
return JSON.parse(window.atob(base64));
} catch (error) {
console.warn('Token解析失败:', error);
console.info('Token解析失败:', error);
return null;
}
}
@ -173,7 +173,7 @@ class TokenService {
// 检查错误响应状态码是否为 401 (未授权)
// 并且确保这不是一个已经重试过的请求 (通过 originalRequest._retry 标记)
if (error.response && error.response.status === 401) {
if (error.response && error.response.status === 401 && !originalRequest.noretry) {
// 统计该请求的重试次数
originalRequest.__retryCount = originalRequest.__retryCount || 0;

Loading…
Cancel
Save