页面.登陆

12003

在本节中,我们将介绍如何实现登陆逻辑。首先,我们将调用登陆接口 login.passwordLogin,然后在登陆成功后,使用浏览器的 cookie 和 localStorage 进行鉴权。

登陆逻辑

  1. 调用登陆接口 login.passwordLogin
  2. 登陆成功后:
    • login.passwordLogin 会自动给浏览器写一个 cookie,用于后续页面访问的鉴权。cookie 的 key 为 ${appId}_authToken,value 为 authToken。
    • 将接口返回的 authTokenuserIddeviceId 持久化到 localStorage,用于后续调用接口的鉴权。

以下是实现登陆逻辑的示例代码:

  1. <jh-toast/>
  2. <v-app>
  3. <v-row class="ma-0 pa-3">
  4. <v-btn class="elevation-0 mr-2" color="success" small @click="doLogin">登陆</v-btn>
  5. </v-row>
  6. </v-app>
  7. <script type="module">
  8. new Vue({
  9. el: '#app',
  10. template: '#app-template',
  11. vuetify: new Vuetify(),
  12. data: {
  13. userId: 'test',
  14. password: '123456',
  15. deviceId: 'test_001',
  16. },
  17. methods: {
  18. async doLogin() {
  19. window.vtoast.loading("登陆");
  20. const resultData = (await window.jianghuAxios({
  21. data: {
  22. appData: {
  23. pageId: 'login',
  24. actionId: 'passwordLogin',
  25. actionData: {
  26. userId: this.userId,
  27. password: this.password,
  28. deviceId: this.deviceId
  29. },
  30. }
  31. }
  32. })).data.appData.resultData;
  33. const { authToken, userId, deviceId } = resultData;
  34. localStorage.setItem(`${window.appInfo.authTokenKey}_authToken`, authToken);
  35. localStorage.setItem(`${window.appInfo.authTokenKey}_userId`, userId);
  36. localStorage.setItem(`${window.appInfo.authTokenKey}_deviceId`, deviceId);
  37. window.vtoast.success("登陆");
  38. },
  39. }
  40. })
  41. </script>

使用 authToken

  • 当用户访问页面时,cookie 会自动传输到服务器。
  • 当用户调用接口时,将 authToken 放入请求体的 data 中。若使用 jianghuAxios 发起接口请求,jianghuAxios 会自动从 localStorage 取 authToken 填入请求体的 data 中。
  1. await window.jianghuAxios({
  2. data: {
  3. authToken: 'xxxxx',
  4. appData: {
  5. pageId: 'test',
  6. actionId: 'selectItemList',
  7. actionData: {},
  8. }
  9. }
  10. })

现在,我们已经成功实现了登陆逻辑,并使用 cookie 和 localStorage 进行鉴权。这使得我们可以在用户访问页面和调用接口时进行有效的身份验证。

完整的登陆案例

  1. <jh-toast />
  2. <jh-mask />
  3. <jh-confirm-dialog />
  4. <v-app>
  5. <v-main>
  6. <v-row class="align-center" style="height: 100vh">
  7. <v-col cols="12" align="center">
  8. <div class="mb-10 text-h7 font-weight-bold success--text">
  9. <$ ctx.app.config.appTitle $>
  10. </div>
  11. <v-card class="login-form-card pa-8">
  12. <v-card-text class="pa-0">
  13. <div class="title">登录您的账户</div>
  14. <v-form ref="loginForm" lazy-validation>
  15. <!-- 表单 [注:登录表单包含密码输入框,chrome密码自动填充的时候会与vuetify的v-input组件样式冲突,使用原生input避免冲突] -->
  16. <v-row class="my-0">
  17. <v-col cols="12">
  18. <input class="jh-cus-input" v-model="userId" placeholder="用户名" />
  19. </v-col>
  20. <v-col cols="12">
  21. <div class="password-wrapper">
  22. <input class="jh-cus-input" v-model="password"
  23. :type="isPasswordShown ? 'text' : 'password'" data-type="password"
  24. @keyup.enter.exact="doUiAction('login')" placeholder="密码" />
  25. <v-icon @click="isPasswordShown = !isPasswordShown" small
  26. class="mdi-eye">{{isPasswordShown ? 'mdi-eye-off-outline' :
  27. 'mdi-eye-outline'}}</v-icon>
  28. </div>
  29. </v-col>
  30. </v-row>
  31. <!-- 操作按钮 -->
  32. <v-row class="my-0 px-3">
  33. <v-btn block color="success" @click="doUiAction('login')" small>登录</v-btn>
  34. </v-row>
  35. </v-form>
  36. </v-card-text>
  37. </v-card>
  38. </v-col>
  39. </v-row>
  40. </v-main>
  41. </v-app>
  42. {% include 'utility/jianghuJs/prepareDeviceIdV4.html' %}
  43. <script type="module">
  44. new Vue({
  45. el: '#app',
  46. template: '#app-template',
  47. vuetify: new Vuetify(),
  48. data: {
  49. userId: '',
  50. password: '',
  51. deviceId: null,
  52. isPasswordShown: false,
  53. },
  54. async created() {},
  55. async mounted() {},
  56. methods: {
  57. async doUiAction(uiActionId, uiActionData) {
  58. try {
  59. switch (uiActionId) {
  60. case 'login':
  61. await this.prepareLoginData();
  62. await this.doLogin();
  63. await this.setLocalStorage();
  64. await this.redirectToPreLoginURL();
  65. break;
  66. default:
  67. console.error("[doUiAction] uiActionId not found", { uiActionId });
  68. break;
  69. }
  70. } catch (error) {
  71. throw error;
  72. } finally {
  73. window.jhMask && window.jhMask.hide();
  74. }
  75. },
  76. // ---------- login uiAction >>>>>>>>>> --------
  77. async prepareLoginData() {
  78. this.deviceId = window.deviceId;
  79. this.userId = _.replace(this.userId, /\s+/g, '');;
  80. this.password = _.toString(this.password);
  81. },
  82. async doLogin() {
  83. try {
  84. const resultData = (await window.jianghuAxios({
  85. data: {
  86. appData: {
  87. pageId: 'login',
  88. actionId: 'passwordLogin',
  89. actionData: {
  90. userId: this.userId,
  91. password: this.password,
  92. deviceId: this.deviceId
  93. },
  94. }
  95. }
  96. })).data.appData.resultData;
  97. this.loginResult = resultData;
  98. window.vtoast.success('登录成功');
  99. } catch (error) {
  100. const { errorCode, errorReason } = error || {};
  101. if (errorCode) {
  102. window.vtoast.fail(errorReason);
  103. throw new Error("登录失败", { errorCode, errorReason });
  104. } else {
  105. window.vtoast.fail('登录失败');
  106. throw new Error("登录失败");
  107. }
  108. }
  109. },
  110. async setLocalStorage() {
  111. localStorage.setItem(`${window.appInfo.authTokenKey}_authToken`, this.loginResult.authToken);
  112. localStorage.setItem(`${window.appInfo.authTokenKey}_userId`, this.loginResult.userId);
  113. localStorage.setItem(`${window.appInfo.authTokenKey}_deviceId`, this.deviceId);
  114. },
  115. async redirectToPreLoginURL() {
  116. // 重定向到帮助页
  117. // window.location.href =`/${window.appInfo.appId}/page/help`;
  118. }
  119. // ---------- <<<<<<<<<<< login uiAction --------
  120. }
  121. })
  122. </script>
  123. <style scoped>
  124. .login-form-card {
  125. width: 400px;
  126. }
  127. /* ---------- 输入框 >>>>>>>>>> -------- */
  128. .jh-cus-input {
  129. border: 1px solid rgba(0, 0, 0, .06);
  130. width: 100%;
  131. height: 32px;
  132. border-radius: 6px;
  133. padding: 0 12px;
  134. color: #5E6278 !important;
  135. outline: none;
  136. font-size: 13px !important;
  137. }
  138. .jh-cus-input:focus,
  139. .jh-cus-input:focus-visible,
  140. input:focus-visible {
  141. border: thin solid #4caf50 !important;
  142. }
  143. /* ---------- <<<<<<<<<<< 输入框 -------- */
  144. /* ---------- 密码框 >>>>>>>>>> -------- */
  145. .password-wrapper {
  146. position: relative;
  147. }
  148. .password-wrapper .mdi-eye {
  149. position: absolute;
  150. right: 8px;
  151. top: 8px;
  152. }
  153. /* ---------- <<<<<<<<<<< 密码框 -------- */
  154. </style>