页面.登陆
12003- 框架登陆页 loginV4.html
- 框架登陆接口 user.js
- 框架用户表 _user
在本节中,我们将介绍如何实现登陆逻辑。首先,我们将调用登陆接口 login.passwordLogin
,然后在登陆成功后,使用浏览器的 cookie 和 localStorage 进行鉴权。
登陆逻辑
- 调用登陆接口
login.passwordLogin
。 - 登陆成功后:
login.passwordLogin
会自动给浏览器写一个 cookie,用于后续页面访问的鉴权。cookie 的 key 为${appId}_authToken
,value 为 authToken。- 将接口返回的
authToken
、userId
和deviceId
持久化到 localStorage,用于后续调用接口的鉴权。
以下是实现登陆逻辑的示例代码:
<jh-toast/>
<v-app>
<v-row class="ma-0 pa-3">
<v-btn class="elevation-0 mr-2" color="success" small @click="doLogin">登陆</v-btn>
</v-row>
</v-app>
<script type="module">
new Vue({
el: '#app',
template: '#app-template',
vuetify: new Vuetify(),
data: {
userId: 'test',
password: '123456',
deviceId: 'test_001',
},
methods: {
async doLogin() {
window.vtoast.loading("登陆");
const resultData = (await window.jianghuAxios({
data: {
appData: {
pageId: 'login',
actionId: 'passwordLogin',
actionData: {
userId: this.userId,
password: this.password,
deviceId: this.deviceId
},
}
}
})).data.appData.resultData;
const { authToken, userId, deviceId } = resultData;
localStorage.setItem(`${window.appInfo.authTokenKey}_authToken`, authToken);
localStorage.setItem(`${window.appInfo.authTokenKey}_userId`, userId);
localStorage.setItem(`${window.appInfo.authTokenKey}_deviceId`, deviceId);
window.vtoast.success("登陆");
},
}
})
</script>
使用 authToken
- 当用户访问页面时,cookie 会自动传输到服务器。
- 当用户调用接口时,将 authToken 放入请求体的 data 中。若使用 jianghuAxios 发起接口请求,jianghuAxios 会自动从 localStorage 取 authToken 填入请求体的 data 中。
await window.jianghuAxios({
data: {
authToken: 'xxxxx',
appData: {
pageId: 'test',
actionId: 'selectItemList',
actionData: {},
}
}
})
现在,我们已经成功实现了登陆逻辑,并使用 cookie 和 localStorage 进行鉴权。这使得我们可以在用户访问页面和调用接口时进行有效的身份验证。
完整的登陆案例
<jh-toast />
<jh-mask />
<jh-confirm-dialog />
<v-app>
<v-main>
<v-row class="align-center" style="height: 100vh">
<v-col cols="12" align="center">
<div class="mb-10 text-h7 font-weight-bold success--text">
<$ ctx.app.config.appTitle $>
</div>
<v-card class="login-form-card pa-8">
<v-card-text class="pa-0">
<div class="title">登录您的账户</div>
<v-form ref="loginForm" lazy-validation>
<!-- 表单 [注:登录表单包含密码输入框,chrome密码自动填充的时候会与vuetify的v-input组件样式冲突,使用原生input避免冲突] -->
<v-row class="my-0">
<v-col cols="12">
<input class="jh-cus-input" v-model="userId" placeholder="用户名" />
</v-col>
<v-col cols="12">
<div class="password-wrapper">
<input class="jh-cus-input" v-model="password"
:type="isPasswordShown ? 'text' : 'password'" data-type="password"
@keyup.enter.exact="doUiAction('login')" placeholder="密码" />
<v-icon @click="isPasswordShown = !isPasswordShown" small
class="mdi-eye">{{isPasswordShown ? 'mdi-eye-off-outline' :
'mdi-eye-outline'}}</v-icon>
</div>
</v-col>
</v-row>
<!-- 操作按钮 -->
<v-row class="my-0 px-3">
<v-btn block color="success" @click="doUiAction('login')" small>登录</v-btn>
</v-row>
</v-form>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-main>
</v-app>
{% include 'utility/jianghuJs/prepareDeviceIdV4.html' %}
<script type="module">
new Vue({
el: '#app',
template: '#app-template',
vuetify: new Vuetify(),
data: {
userId: '',
password: '',
deviceId: null,
isPasswordShown: false,
},
async created() {},
async mounted() {},
methods: {
async doUiAction(uiActionId, uiActionData) {
try {
switch (uiActionId) {
case 'login':
await this.prepareLoginData();
await this.doLogin();
await this.setLocalStorage();
await this.redirectToPreLoginURL();
break;
default:
console.error("[doUiAction] uiActionId not found", { uiActionId });
break;
}
} catch (error) {
throw error;
} finally {
window.jhMask && window.jhMask.hide();
}
},
// ---------- login uiAction >>>>>>>>>> --------
async prepareLoginData() {
this.deviceId = window.deviceId;
this.userId = _.replace(this.userId, /\s+/g, '');;
this.password = _.toString(this.password);
},
async doLogin() {
try {
const resultData = (await window.jianghuAxios({
data: {
appData: {
pageId: 'login',
actionId: 'passwordLogin',
actionData: {
userId: this.userId,
password: this.password,
deviceId: this.deviceId
},
}
}
})).data.appData.resultData;
this.loginResult = resultData;
window.vtoast.success('登录成功');
} catch (error) {
const { errorCode, errorReason } = error || {};
if (errorCode) {
window.vtoast.fail(errorReason);
throw new Error("登录失败", { errorCode, errorReason });
} else {
window.vtoast.fail('登录失败');
throw new Error("登录失败");
}
}
},
async setLocalStorage() {
localStorage.setItem(`${window.appInfo.authTokenKey}_authToken`, this.loginResult.authToken);
localStorage.setItem(`${window.appInfo.authTokenKey}_userId`, this.loginResult.userId);
localStorage.setItem(`${window.appInfo.authTokenKey}_deviceId`, this.deviceId);
},
async redirectToPreLoginURL() {
// 重定向到帮助页
// window.location.href =`/${window.appInfo.appId}/page/help`;
}
// ---------- <<<<<<<<<<< login uiAction --------
}
})
</script>
<style scoped>
.login-form-card {
width: 400px;
}
/* ---------- 输入框 >>>>>>>>>> -------- */
.jh-cus-input {
border: 1px solid rgba(0, 0, 0, .06);
width: 100%;
height: 32px;
border-radius: 6px;
padding: 0 12px;
color: #5E6278 !important;
outline: none;
font-size: 13px !important;
}
.jh-cus-input:focus,
.jh-cus-input:focus-visible,
input:focus-visible {
border: thin solid #4caf50 !important;
}
/* ---------- <<<<<<<<<<< 输入框 -------- */
/* ---------- 密码框 >>>>>>>>>> -------- */
.password-wrapper {
position: relative;
}
.password-wrapper .mdi-eye {
position: absolute;
right: 8px;
top: 8px;
}
/* ---------- <<<<<<<<<<< 密码框 -------- */
</style>