Page Login

12003

In this section, we’ll explain how to implement the login flow. First, call the login API login.passwordLogin. After a successful login, use the browser’s cookies and localStorage for authentication.

Login Flow

  1. Call the login API login.passwordLogin.

  2. On success:

    • login.passwordLogin automatically writes a cookie to the browser for subsequent page access authentication. The cookie key is ${appId}_authToken, and the value is the authToken.
    • Persist the returned authToken, userId, and deviceId to localStorage for authenticating future API calls.

Below is an example implementation of the login logic:

  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>

Using authToken

  • When the user visits a page, the cookie is sent to the server automatically.
  • When the user calls an API, include the authToken in the request body under data. If you use jianghuAxios to make API requests, it will automatically read the authToken from localStorage and inject it into data.
  1. await window.jianghuAxios({
  2. data: {
  3. authToken: 'xxxxx',
  4. appData: {
  5. pageId: 'test',
  6. actionId: 'selectItemList',
  7. actionData: {},
  8. }
  9. }
  10. })

With this, you’ve implemented the login flow and set up authentication using cookies and localStorage, enabling reliable identity verification for both page visits and API calls.

Full Login Example

  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>