Sfoglia il codice sorgente

feat(profile): password security verification

8 to 20 digits and containing at least two types of numbers, letters, and characters
Casper Dai 2 anni fa
parent
commit
42fc64d3a5

+ 2 - 2
src/api/user.js

@@ -18,14 +18,14 @@ import {
 
 export function getWechatQr (id) {
   return request({
-    url: `keycloak/query/ticket/${id}`,
+    url: `/keycloak/query/ticket/${id}`,
     method: 'GET'
   })
 }
 
 export function getAppletQr (data) {
   return request({
-    url: 'wxapplet/qrcode',
+    url: '/wxapplet/qrcode',
     method: 'GET',
     params: data
   })

+ 35 - 15
src/layout/components/Navbar/Profile/components/ResetPassword.vue

@@ -1,12 +1,16 @@
 <template>
   <div class="c-grid-form auto">
     <span class="c-grid-form__label u-bold u-required">新密码</span>
-    <el-input
-      v-model.trim="newPassword"
-      class="u-width--sm u-password"
-      placeholder="6-20位密码"
-      maxlength="20"
-    />
+    <div class="l-flex--col">
+      <el-input
+        v-model.trim="newPassword"
+        class="c-sibling-item--v u-width--sm u-password"
+        placeholder="8-20位密码"
+        maxlength="20"
+      />
+      <div class="c-sibling-item--v nearer u-font-size--xs u-color--info">至少包含数字、字母、字符中的两种</div>
+      <div class="c-sibling-item--v nearer u-font-size--xs u-color--info">字符范围:+_!@#$%^&*.,?</div>
+    </div>
     <span class="c-grid-form__label u-bold u-required">确认新密码</span>
     <el-input
       v-model.trim="confirmNewPassword"
@@ -43,17 +47,11 @@ export default {
   },
   methods: {
     onSave () {
-      if (!this.newPassword) {
-        this.$message({
-          type: 'warning',
-          message: '请填写新密码'
-        })
-        return
-      }
-      if (this.newPassword.length < 6) {
+      const errMsg = this.checkPassword(this.newPassword)
+      if (errMsg) {
         this.$message({
           type: 'warning',
-          message: '请填写至少6位的密码'
+          message: errMsg
         })
         return
       }
@@ -68,6 +66,28 @@ export default {
         this.newPassword = ''
         this.confirmNewPassword = ''
       })
+    },
+    checkPassword (password) {
+      if (password.length < 8) {
+        return '密码最少8位'
+      }
+      if (!/^[\da-zA-Z+_!@#$%^&*.,?]+$/.test(password)) {
+        return '包含非法字符'
+      }
+      let has = 0
+      if (/\d/.test(password)) {
+        has += 1
+      }
+      if (/[a-zA-Z]/.test(password)) {
+        has += 1
+      }
+      if (/[+_!@#$%^&*.,?]/.test(password)) {
+        has += 1
+      }
+      if (has < 2) {
+        return '不符合最低安全要求'
+      }
+      return ''
     }
   }
 }

+ 115 - 102
src/layout/components/Navbar/Profile/index.vue

@@ -28,121 +28,129 @@
         @click="onClose"
       />
     </div>
-    <div
-      v-loading="loading"
-      class="l-flex__fill l-flex--col"
-    >
-      <el-tabs
-        v-model="active"
-        class="c-tabs has-padding"
-        stretch
-      >
-        <el-tab-pane
-          class="c-sibling-item"
-          label="账号设置"
-          name="settings"
-        />
-        <el-tab-pane
-          class="c-sibling-item far"
-          label="密码重置"
-          name="password"
-        />
-      </el-tabs>
-      <warning
-        v-if="error"
-        class=" u-align-self--center"
-        @click="getUserInfo"
-      />
+    <template v-if="hasSupportFeature">
       <div
-        v-else
-        class="l-flex__fill l-flex--col center has-padding u-overflow-y--auto"
+        v-loading="loading"
+        class="l-flex__fill l-flex--col"
       >
-        <template v-if="active === 'settings'">
-          <user-info-item
-            v-if="user && supportFeature.note"
-            key="phone"
-            class="c-sibling-item--v"
-            type="phone"
-            :initial="phone"
-            @update="onChangePhone"
+        <el-tabs
+          v-model="active"
+          class="c-tabs has-padding"
+          stretch
+        >
+          <el-tab-pane
+            class="c-sibling-item"
+            label="账号设置"
+            name="settings"
           />
-          <user-info-item
-            v-if="user && supportFeature.email"
-            key="email"
-            class="c-sibling-item--v"
-            type="email"
-            :initial="email"
-            @update="onChangeEmail"
+          <el-tab-pane
+            class="c-sibling-item far"
+            label="密码重置"
+            name="password"
           />
-          <div
-            v-if="supportFeature.wechat"
-            class="l-flex--row center c-sibling-item--v farther"
-          >
+        </el-tabs>
+        <warning
+          v-if="error"
+          class=" u-align-self--center"
+          @click="getUserInfo"
+        />
+        <div
+          v-else
+          class="l-flex__fill l-flex--col center has-padding u-overflow-y--auto"
+        >
+          <template v-if="active === 'settings'">
+            <user-info-item
+              v-if="user && supportFeature.note"
+              key="phone"
+              class="c-sibling-item--v"
+              type="phone"
+              :initial="phone"
+              @update="onChangePhone"
+            />
+            <user-info-item
+              v-if="user && supportFeature.email"
+              key="email"
+              class="c-sibling-item--v"
+              type="email"
+              :initial="email"
+              @update="onChangeEmail"
+            />
             <div
-              class="o-app has-active"
-              @click="onClickWechat"
+              v-if="supportFeature.wechat"
+              class="l-flex--row center c-sibling-item--v farther"
             >
-              <i class="c-sibling-item--v o-icon md wechat" />
-              <div class="l-flex--row c-sibling-item--v far">
-                <template v-if="wechat">
-                  <div class="c-sibling-item u-color--info light">已绑定</div>
-                  <div class="c-sibling-item">解绑</div>
-                </template>
-                <div v-else>
-                  绑定微信
+              <div
+                class="o-app has-active"
+                @click="onClickWechat"
+              >
+                <i class="c-sibling-item--v o-icon md wechat" />
+                <div class="l-flex--row c-sibling-item--v far">
+                  <template v-if="wechat">
+                    <div class="c-sibling-item u-color--info light">已绑定</div>
+                    <div class="c-sibling-item">解绑</div>
+                  </template>
+                  <div v-else>
+                    绑定微信
+                  </div>
                 </div>
               </div>
-            </div>
-            <div
-              class="o-app has-active"
-              @click="onClickApplet"
-            >
-              <i class="c-sibling-item--v o-icon md applet" />
-              <div class="l-flex--row c-sibling-item--v far">
-                <template v-if="applet">
-                  <div class="c-sibling-item u-color--info light">已绑定</div>
-                  <div class="c-sibling-item">解绑</div>
-                </template>
-                <div v-else>
-                  绑定小程序
+              <div
+                class="o-app has-active"
+                @click="onClickApplet"
+              >
+                <i class="c-sibling-item--v o-icon md applet" />
+                <div class="l-flex--row c-sibling-item--v far">
+                  <template v-if="applet">
+                    <div class="c-sibling-item u-color--info light">已绑定</div>
+                    <div class="c-sibling-item">解绑</div>
+                  </template>
+                  <div v-else>
+                    绑定小程序
+                  </div>
                 </div>
               </div>
             </div>
-          </div>
-        </template>
-        <template v-if="active === 'password'">
-          <reset-password />
-        </template>
-      </div>
-    </div>
-    <el-dialog
-      :visible.sync="showQr"
-      custom-class="c-dialog--transparent"
-      :close-on-click-modal="false"
-      append-to-body
-    >
-      <div class="c-wechat has-padding">
-        <div class="l-flex--row center c-sibling-item--v u-relative">
-          <i class="c-sibling-item o-icon wechat" />
-          <span class="c-sibling-item u-color--black u-bold">请使用微信扫一扫</span>
-          <i
-            class="c-wechat__close el-icon-close o-icon has-active u-bold"
-            @click="onCloseQr"
-          />
+          </template>
+          <template v-if="active === 'password'">
+            <reset-password />
+          </template>
         </div>
-        <div
-          v-loading="qrOptions.loading"
-          class="c-sibling-item--v far c-wechat__wrapper"
-          :class="{ retry: qrOptions.retry }"
-          @click="getQr"
-        >
-          <img
-            class="c-wechat__qr"
-            :src="qrOptions.qr"
+      </div>
+      <el-dialog
+        :visible.sync="showQr"
+        custom-class="c-dialog--transparent"
+        :close-on-click-modal="false"
+        append-to-body
+      >
+        <div class="c-wechat has-padding">
+          <div class="l-flex--row center c-sibling-item--v u-relative">
+            <i class="c-sibling-item o-icon wechat" />
+            <span class="c-sibling-item u-color--black u-bold">请使用微信扫一扫</span>
+            <i
+              class="c-wechat__close el-icon-close o-icon has-active u-bold"
+              @click="onCloseQr"
+            />
+          </div>
+          <div
+            v-loading="qrOptions.loading"
+            class="c-sibling-item--v far c-wechat__wrapper"
+            :class="{ retry: qrOptions.retry }"
+            @click="getQr"
           >
+            <img
+              class="c-wechat__qr"
+              :src="qrOptions.qr"
+            >
+          </div>
         </div>
-      </div>
-    </el-dialog>
+      </el-dialog>
+    </template>
+    <div
+      v-else
+      class="l-flex__fill l-flex--col center has-padding"
+    >
+      <reset-password />
+    </div>
   </div>
 </template>
 
@@ -202,6 +210,9 @@ export default {
       const avatar = this.avatar
       return avatar ? { backgroundImage: `url("${avatar}")` } : null
     },
+    hasSupportFeature () {
+      return SupportedAlarmStrategies.some(({ support }) => support)
+    },
     supportFeature () {
       const map = {}
       SupportedAlarmStrategies.forEach(({ key, support }) => {
@@ -240,7 +251,9 @@ export default {
   created () {
     this.$timer = null
     this.$checkTimer = null
-    this.getUserInfo()
+    if (this.hasSupportFeature) {
+      this.getUserInfo()
+    }
   },
   beforeDestroy () {
     clearTimeout(this.$timer)