xiongzhu 4 years ago
parent
commit
0e83d7b103

+ 9 - 2
src/main/data-center-admin/src/plugins/http.js

@@ -118,17 +118,24 @@ export default {
                 options = options || {};
                 options = options || {};
                 options.headers = options.headers || {};
                 options.headers = options.headers || {};
                 body = body || {};
                 body = body || {};
-                if (!(body instanceof FormData)) {
+                if (body instanceof FormData) {
+                } else {
+                    options.headers['Content-Encrypted'] = 'true';
                     if (options.body === 'json') {
                     if (options.body === 'json') {
                         body = encrypt(JSON.stringify(body));
                         body = encrypt(JSON.stringify(body));
                         options.headers['content-type'] = 'application/json';
                         options.headers['content-type'] = 'application/json';
                     } else {
                     } else {
+                        for (let key of Object.keys(body)) {
+                            if (body[key] != undefined && body[key] !== '') {
+                                body[key] = encrypt(body[key] + '');
+                            }
+                        }
                         body = qs.stringify(body);
                         body = qs.stringify(body);
                     }
                     }
                 }
                 }
                 return new Promise((resolve, reject) => {
                 return new Promise((resolve, reject) => {
                     axiosInstance
                     axiosInstance
-                        .post(url, body, { withCredentials: true })
+                        .post(url, body, { withCredentials: true, headers: options.headers })
                         .then(res => {
                         .then(res => {
                             resolve(res.data);
                             resolve(res.data);
                         })
                         })

+ 18 - 3
src/main/data-center-admin/src/views/Admin.vue

@@ -80,6 +80,7 @@
 <script>
 <script>
 import SysMenu from '../components/SysMenu';
 import SysMenu from '../components/SysMenu';
 import { mapState } from 'vuex';
 import { mapState } from 'vuex';
+import { isBefore, parse, format, addDays } from 'date-fns';
 export default {
 export default {
     name: 'admin',
     name: 'admin',
     created() {
     created() {
@@ -122,6 +123,13 @@ export default {
             .catch(e => {
             .catch(e => {
                 console.log(e);
                 console.log(e);
             });
             });
+
+        let pwdUpdateAt = this.userInfo.pwdUpdateAt;
+        if (!pwdUpdateAt || isBefore(parse(pwdUpdateAt, 'yyyy-MM-dd HH:mm:ss', new Date()), addDays(new Date(), -30))) {
+            this.$alert('您已30天未修改密码,请尽快修改密码', '提示').then(() => {
+                this.onShowPwdDialog();
+            });
+        }
     },
     },
     data() {
     data() {
         return {
         return {
@@ -140,7 +148,14 @@ export default {
             },
             },
             captcha: '',
             captcha: '',
             pwdRules: {
             pwdRules: {
-                password: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
+                password: [
+                    { required: true, message: '请输入新密码', trigger: 'blur' },
+                    {
+                        pattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                        message: '密码必须包含大小写字母和数字,且长度为8-30',
+                        trigger: 'blur'
+                    }
+                ],
                 repeat: [
                 repeat: [
                     { required: true, message: '请确认新密码', trigger: 'blur' },
                     { required: true, message: '请确认新密码', trigger: 'blur' },
                     {
                     {
@@ -302,8 +317,8 @@ export default {
             this.$refs.pwdForm.validate(valid => {
             this.$refs.pwdForm.validate(valid => {
                 if (valid) {
                 if (valid) {
                     this.$http
                     this.$http
-                        .post('/user/setPassword', {
-                            password: this.pwdForm.password
+                        .post('/user/changePassword', {
+                            ...this.pwdForm
                         })
                         })
                         .then(res => {
                         .then(res => {
                             console.log(res);
                             console.log(res);

+ 82 - 25
src/main/data-center-admin/src/views/Login.vue

@@ -2,21 +2,24 @@
     <div class="container" :style="{ backgroundImage: 'url(' + require('../assets/bg_login.jpg') + ')' }">
     <div class="container" :style="{ backgroundImage: 'url(' + require('../assets/bg_login.jpg') + ')' }">
         <transition :name="`slide-${register ? 'in' : 'out'}`">
         <transition :name="`slide-${register ? 'in' : 'out'}`">
             <div class="login-wrapper" @keyup.enter="doRegister" v-if="register" key="register">
             <div class="login-wrapper" @keyup.enter="doRegister" v-if="register" key="register">
-                <el-page-header @back="register = false" title="登录" style="width: 350px; line-height: 60px;">
+                <el-page-header @back="register = false" title="登录" style="width: 350px; line-height: 60px">
                     <div class="register-title" slot="content">注册账号</div>
                     <div class="register-title" slot="content">注册账号</div>
                 </el-page-header>
                 </el-page-header>
 
 
-                <el-form :model="registerInfo" style="width: 350px;" ref="registerForm">
-                    <el-form-item prop="username" :rules="{ required: true, message: '请输入用户名', trigger: 'blur' }">
+                <el-form :model="registerInfo" style="width: 350px" ref="registerForm" :rules="registerRules">
+                    <el-form-item prop="username">
                         <el-input v-model="registerInfo.username" placeholder="用户名"> </el-input>
                         <el-input v-model="registerInfo.username" placeholder="用户名"> </el-input>
                     </el-form-item>
                     </el-form-item>
-                    <el-form-item prop="password" :rules="{ required: true, message: '请输入密码', trigger: 'blur' }">
+                    <el-form-item prop="password">
                         <el-input v-model="registerInfo.password" placeholder="密码" type="password"></el-input>
                         <el-input v-model="registerInfo.password" placeholder="密码" type="password"></el-input>
                     </el-form-item>
                     </el-form-item>
+                    <el-form-item prop="password1">
+                        <el-input v-model="registerInfo.password1" placeholder="重复密码" type="password"></el-input>
+                    </el-form-item>
                     <el-form-item>
                     <el-form-item>
-                        <el-button :loading="loading" @click="doRegister" type="primary" style="width: 100%;"
-                            >注册</el-button
-                        >
+                        <el-button :loading="loading" @click="doRegister" type="primary" style="width: 100%">
+                            注册
+                        </el-button>
                     </el-form-item>
                     </el-form-item>
                 </el-form>
                 </el-form>
             </div>
             </div>
@@ -24,21 +27,15 @@
                 <div class="title">欢迎登录</div>
                 <div class="title">欢迎登录</div>
                 <el-tabs v-model="activeName" stretch class="tab-list">
                 <el-tabs v-model="activeName" stretch class="tab-list">
                     <el-tab-pane label="用户名登陆" name="first">
                     <el-tab-pane label="用户名登陆" name="first">
-                        <el-form :model="userInfo" style="width: 350px;" ref="form">
-                            <el-form-item
-                                prop="username"
-                                :rules="{ required: true, message: '请输入用户名', trigger: 'blur' }"
-                            >
+                        <el-form :model="userInfo" style="width: 350px" ref="form" :rules="loginRules">
+                            <el-form-item prop="username">
                                 <el-input v-model="userInfo.username" placeholder="用户名"> </el-input>
                                 <el-input v-model="userInfo.username" placeholder="用户名"> </el-input>
                             </el-form-item>
                             </el-form-item>
-                            <el-form-item
-                                prop="password"
-                                :rules="{ required: true, message: '请输入密码', trigger: 'blur' }"
-                            >
+                            <el-form-item prop="password">
                                 <el-input v-model="userInfo.password" placeholder="密码" type="password"></el-input>
                                 <el-input v-model="userInfo.password" placeholder="密码" type="password"></el-input>
                             </el-form-item>
                             </el-form-item>
                             <el-form-item>
                             <el-form-item>
-                                <el-button :loading="loading" @click="login" type="primary" style="width: 100%;"
+                                <el-button :loading="loading" @click="login" type="primary" style="width: 100%"
                                     >登录
                                     >登录
                                 </el-button>
                                 </el-button>
                             </el-form-item>
                             </el-form-item>
@@ -48,15 +45,15 @@
                                     style="width: 100%;">手机登录</el-button>
                                     style="width: 100%;">手机登录</el-button>
                             </el-form-item> -->
                             </el-form-item> -->
                             <el-form-item label="">
                             <el-form-item label="">
-                                <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox>
-                                <el-button type="text" style="float: right;" @click="register = true"
+                                <!-- <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox> -->
+                                <el-button type="text" style="float: right" @click="register = true"
                                     >注册账号
                                     >注册账号
                                 </el-button>
                                 </el-button>
                             </el-form-item>
                             </el-form-item>
                         </el-form>
                         </el-form>
                     </el-tab-pane>
                     </el-tab-pane>
-                    <el-tab-pane label="验证码登陆" name="second">
-                        <el-form :model="userInfo" style="width: 350px;" ref="form2">
+                    <el-tab-pane label="验证码登陆" name="second" disabled>
+                        <el-form :model="userInfo" style="width: 350px" ref="form2">
                             <el-form-item
                             <el-form-item
                                 prop="phone"
                                 prop="phone"
                                 :rules="{ required: true, message: '请输入手机号', trigger: 'blur' }"
                                 :rules="{ required: true, message: '请输入手机号', trigger: 'blur' }"
@@ -83,13 +80,13 @@
                                 </el-button>
                                 </el-button>
                             </el-form-item> -->
                             </el-form-item> -->
                             <el-form-item>
                             <el-form-item>
-                                <el-button :loading="loading" @click="phonelogin" type="primary" style="width: 100%;"
+                                <el-button :loading="loading" @click="phonelogin" type="primary" style="width: 100%"
                                     >手机登录</el-button
                                     >手机登录</el-button
                                 >
                                 >
                             </el-form-item>
                             </el-form-item>
                             <el-form-item label="">
                             <el-form-item label="">
                                 <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox>
                                 <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox>
-                                <el-button type="text" style="float: right;" @click="register = true"
+                                <el-button type="text" style="float: right" @click="register = true"
                                     >注册账号
                                     >注册账号
                                 </el-button>
                                 </el-button>
                             </el-form-item>
                             </el-form-item>
@@ -101,6 +98,7 @@
     </div>
     </div>
 </template>
 </template>
 <script>
 <script>
+import { parse, format, isBefore, addMinutes } from 'date-fns';
 export default {
 export default {
     data() {
     data() {
         return {
         return {
@@ -119,12 +117,66 @@ export default {
             },
             },
             activeName: 'first',
             activeName: 'first',
             time: 0,
             time: 0,
-            sending: false
+            sending: false,
+            registerRules: {
+                username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+                password: [
+                    { required: true, message: '请输入密码', trigger: 'blur' },
+                    {
+                        pattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                        message: '密码必须包含大小写字母和数字,且长度为8-30',
+                        trigger: 'blur'
+                    }
+                ],
+                password1: [
+                    {
+                        validator: (rule, value, callback) => {
+                            if (value !== this.registerInfo.password) {
+                                callback(new Error('两次输入密码不一致!'));
+                            } else {
+                                callback();
+                            }
+                        }
+                    }
+                ]
+            },
+            loginRules: {
+                username: [
+                    { required: true, message: '请输入用户名', trigger: 'blur' },
+                    {
+                        validator: (rule, value, callback) => {
+                            if (
+                                value !== '' &&
+                                /[`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%&*()\-+={}|《》?:“”【】、;‘’,。、]/im.test(
+                                    value
+                                )
+                            ) {
+                                callback(new Error('用户名输入不正确'));
+                            } else {
+                                callback();
+                            }
+                        }
+                    }
+                ],
+                password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
+            }
         };
         };
     },
     },
     methods: {
     methods: {
         login() {
         login() {
             this.$refs.form.validate(valid => {
             this.$refs.form.validate(valid => {
+                let tryNum = parseInt(localStorage.getItem('loginTry::' + this.userInfo.username) || 0);
+
+                let lastLogin = localStorage.getItem('lastLogin');
+                lastLogin = parse(lastLogin, 'yyyy-MM-dd HH:mm:ss', new Date());
+                if (isBefore(lastLogin, addMinutes(new Date(), -30))) {
+                    tryNum = 0;
+                } else if (tryNum >= 5) {
+                    this.$message.error('密码错误超过5次,账号已锁定');
+                    return;
+                }
+                localStorage.setItem('loginTry::' + this.userInfo.username, ++tryNum);
+                localStorage.setItem('lastLogin', format(new Date(), 'yyyy-MM-dd HH:mm:ss'));
                 if (valid) {
                 if (valid) {
                     this.loading = true;
                     this.loading = true;
                     this.$http
                     this.$http
@@ -143,11 +195,16 @@ export default {
                             this.$router.replace({
                             this.$router.replace({
                                 name: this.$route.params.name || 'chart1'
                                 name: this.$route.params.name || 'chart1'
                             });
                             });
+                            localStorage.removeItem('loginTry::' + this.userInfo.username);
                         })
                         })
                         .catch(e => {
                         .catch(e => {
                             console.log(e);
                             console.log(e);
                             this.loading = false;
                             this.loading = false;
-                            this.$message.error(e.error);
+                            if (tryNum >= 5) {
+                                this.$message.error('密码错误超过5次,账号已锁定');
+                            } else {
+                                this.$message.error(e.error + ',剩余尝试次数' + (5 - tryNum));
+                            }
                         });
                         });
                 }
                 }
             });
             });

+ 9 - 2
src/main/data-center-admin/src/views/UserEdit.vue

@@ -6,7 +6,7 @@
             ref="form"
             ref="form"
             label-width="80px"
             label-width="80px"
             label-position="right"
             label-position="right"
-            style="max-width: 500px;"
+            style="max-width: 500px"
         >
         >
             <el-form-item prop="avatar" label="头像">
             <el-form-item prop="avatar" label="头像">
                 <crop-upload v-model="formData.avatar"></crop-upload>
                 <crop-upload v-model="formData.avatar"></crop-upload>
@@ -150,7 +150,14 @@ export default {
                 .catch(() => {});
                 .catch(() => {});
         },
         },
         resetPassword() {
         resetPassword() {
-            this.$prompt('请输入新密码', '重置密码', { inputType: 'password' })
+            this.$prompt('请输入新密码', '重置密码', {
+                inputType: 'password',
+                inputType: 'password',
+                inputPattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                inputErrorMessage: '密码必须包含大小写字母和数字,且长度为8-30',
+                closeOnClickModal: false,
+                closeOnPressEscape: false
+            })
                 .then(res => {
                 .then(res => {
                     console.log(res);
                     console.log(res);
                     if (res.value) {
                     if (res.value) {

+ 10 - 2
src/main/data-center/src/plugins/http.js

@@ -105,17 +105,25 @@ export default {
                 options = options || {};
                 options = options || {};
                 options.headers = options.headers || {};
                 options.headers = options.headers || {};
                 body = body || {};
                 body = body || {};
-                if (!(body instanceof FormData)) {
+                if (body instanceof FormData) {
+                    //
+                } else {
+                    options.headers['Content-Encrypted'] = 'true';
                     if (options.body === 'json') {
                     if (options.body === 'json') {
                         body = encrypt(JSON.stringify(body));
                         body = encrypt(JSON.stringify(body));
                         options.headers['content-type'] = 'application/json';
                         options.headers['content-type'] = 'application/json';
                     } else {
                     } else {
+                        for (let key of Object.keys(body)) {
+                            if (body[key] != undefined && body[key] !== '') {
+                                body[key] = encrypt(body[key] + '');
+                            }
+                        }
                         body = qs.stringify(body);
                         body = qs.stringify(body);
                     }
                     }
                 }
                 }
                 return new Promise((resolve, reject) => {
                 return new Promise((resolve, reject) => {
                     axiosInstance
                     axiosInstance
-                        .post(url, body, { withCredentials: true })
+                        .post(url, body, { withCredentials: true, headers: { ...options.headers } })
                         .then((res) => {
                         .then((res) => {
                             resolve(res.data);
                             resolve(res.data);
                         })
                         })

+ 1 - 1
src/main/java/cn/licoy/encryptbody/advice/DecryptRequestBodyAdvice.java

@@ -39,7 +39,7 @@ import java.util.List;
 /**
 /**
  * 请求数据的加密信息解密处理<br>
  * 请求数据的加密信息解密处理<br>
  * 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.RequestBody}</strong>
  * 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.RequestBody}</strong>
- * 以及package为<strong><code>cn.licoy.encryptbody.annotation.decrypt</code></strong>下的注解有效
+ * 以及package为<strong>{@link cn.licoy.encryptbody.annotation.decrypt}</strong>下的注解有效
  *
  *
  * @author licoy.cn
  * @author licoy.cn
  * @version 2018/9/7
  * @version 2018/9/7

+ 84 - 0
src/main/java/com/izouma/jmrh/config/DecryptFilter.java

@@ -0,0 +1,84 @@
+package com.izouma.jmrh.config;
+
+import cn.licoy.encryptbody.config.EncryptBodyConfig;
+import cn.licoy.encryptbody.util.AESEncryptUtil;
+import cn.licoy.encryptbody.util.CheckUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.IOException;
+import java.util.Map;
+
+@Component
+@Order(1)
+@AllArgsConstructor
+@Slf4j
+public class DecryptFilter implements Filter {
+
+    private final EncryptBodyConfig config;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        Filter.super.init(filterConfig);
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        if ("true".equals(((HttpServletRequest) servletRequest).getHeader("content-encrypted"))) {
+            filterChain.doFilter(new EncryptedRequest(servletRequest, config), servletResponse);
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    @Override
+    public void destroy() {
+        Filter.super.destroy();
+    }
+
+    @Slf4j
+    static class EncryptedRequest extends HttpServletRequestWrapper {
+
+        private final EncryptBodyConfig config;
+
+        public EncryptedRequest(ServletRequest request, EncryptBodyConfig config) {
+            super((HttpServletRequest) request);
+            this.config = config;
+        }
+
+
+        public String getParameter(String paramName) {
+            String value = super.getParameter(paramName);
+            if (StringUtils.isNotBlank(value)) {
+                try {
+                    AESEncryptUtil.decrypt(value, config.getAesKey());
+                } catch (Exception e) {
+                    log.error("解密失败", e);
+                }
+            }
+            return value;
+        }
+
+        public String[] getParameterValues(String paramName) {
+            String[] values = super.getParameterValues(paramName);
+            if (values != null) {
+                for (int index = 0; index < values.length; index++) {
+                    if (StringUtils.isNotBlank(values[index])) {
+                        try {
+                            values[index] = AESEncryptUtil.decrypt(values[index], config.getAesKey());
+                        } catch (Exception e) {
+                            log.error("解密失败", e);
+                        }
+                    }
+                }
+            }
+            return values;
+        }
+    }
+}

+ 2 - 2
src/main/java/com/izouma/jmrh/config/WebMvcConfig.java

@@ -69,9 +69,9 @@ public class WebMvcConfig implements WebMvcConfigurer {
     @Override
     @Override
     public void addCorsMappings(CorsRegistry registry) {
     public void addCorsMappings(CorsRegistry registry) {
         registry.addMapping("/**")
         registry.addMapping("/**")
-                .allowedHeaders("*")
                 .allowCredentials(true)
                 .allowCredentials(true)
-                .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH")
+                .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH", "OPTION")
+                .allowedOrigins("*")
                 .exposedHeaders("Content-Disposition,Content-Encrypted");
                 .exposedHeaders("Content-Disposition,Content-Encrypted");
     }
     }
 
 

+ 2 - 0
src/main/java/com/izouma/jmrh/domain/User.java

@@ -89,4 +89,6 @@ public class User extends BaseEntity implements Serializable {
 
 
     private String orgName;
     private String orgName;
     /*实名认证信息*/
     /*实名认证信息*/
+
+    private LocalDateTime pwdUpdateAt;
 }
 }

+ 11 - 0
src/main/java/com/izouma/jmrh/service/UserService.java

@@ -25,6 +25,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RequestParam;
 
 
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.*;
 
 
 @Service
 @Service
@@ -38,6 +39,7 @@ public class UserService {
     private StorageService  storageService;
     private StorageService  storageService;
     private JwtTokenUtil    jwtTokenUtil;
     private JwtTokenUtil    jwtTokenUtil;
     private MailCodeService mailCodeService;
     private MailCodeService mailCodeService;
+    private CaptchaService  captchaService;
 
 
     public User loginByPhone(String phone, String code, String password) {
     public User loginByPhone(String phone, String code, String password) {
         User user = userRepo.findByPhone(phone);
         User user = userRepo.findByPhone(phone);
@@ -160,6 +162,7 @@ public class UserService {
     public String setPassword(Long userId, String password) {
     public String setPassword(Long userId, String password) {
         User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
         User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
         user.setPassword(new BCryptPasswordEncoder().encode(password));
         user.setPassword(new BCryptPasswordEncoder().encode(password));
+        user.setPwdUpdateAt(LocalDateTime.now());
         user = userRepo.save(user);
         user = userRepo.save(user);
         return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
         return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
     }
     }
@@ -205,6 +208,14 @@ public class UserService {
             throw new BusinessException("此邮箱未注册");
             throw new BusinessException("此邮箱未注册");
         }
         }
         user.setPassword(new BCryptPasswordEncoder().encode(password));
         user.setPassword(new BCryptPasswordEncoder().encode(password));
+        user.setPwdUpdateAt(LocalDateTime.now());
         userRepo.save(user);
         userRepo.save(user);
     }
     }
+
+    public String setPasswordCaptcha(Long userId, String key, String code, String password) {
+        if (!captchaService.verify(key, code)) {
+            throw new BusinessException("验证码错误");
+        }
+        return setPassword(userId, password);
+    }
 }
 }

+ 4 - 0
src/main/java/com/izouma/jmrh/web/UserController.java

@@ -192,4 +192,8 @@ public class UserController extends BaseController {
                 contactPhone, email, idNo);
                 contactPhone, email, idNo);
     }
     }
 
 
+    @PostMapping("/changePassword")
+    public String changePassword(@RequestParam String password, @RequestParam String key, @RequestParam String code) {
+        return userService.setPasswordCaptcha(SecurityUtils.getAuthenticatedUser().getId(), key, code, password);
+    }
 }
 }

+ 9 - 1
src/main/jmrh/src/plugins/http.js

@@ -104,11 +104,19 @@ export default {
                 options = options || {};
                 options = options || {};
                 options.headers = options.headers || {};
                 options.headers = options.headers || {};
                 body = body || {};
                 body = body || {};
-                if (!(body instanceof FormData)) {
+                if (body instanceof FormData) {
+                    //
+                } else {
+                    options.headers['Content-Encrypted'] = 'true';
                     if (options.body === 'json') {
                     if (options.body === 'json') {
                         body = encrypt(JSON.stringify(body));
                         body = encrypt(JSON.stringify(body));
                         options.headers['content-type'] = 'application/json';
                         options.headers['content-type'] = 'application/json';
                     } else {
                     } else {
+                        for (let key of Object.keys(body)) {
+                            if (body[key] != undefined && body[key] !== '') {
+                                body[key] = encrypt(body[key] + '');
+                            }
+                        }
                         body = qs.stringify(body);
                         body = qs.stringify(body);
                     }
                     }
                 }
                 }

+ 7 - 3
src/main/jmrh/src/views/login/register.vue

@@ -2,7 +2,7 @@
     <div class="login">
     <div class="login">
         <div class="login_title">
         <div class="login_title">
             <span class="title">注册账号</span>
             <span class="title">注册账号</span>
-            <span style="color: #C8C9CC;cursor: pointer;" @click="$router.push({ name: 'home' })">返回首页</span>
+            <span style="color: #c8c9cc; cursor: pointer" @click="$router.push({ name: 'home' })">返回首页</span>
         </div>
         </div>
         <div class="form">
         <div class="form">
             <el-form :model="formData" :rules="rules" label-width="80px" label-position="right" ref="form">
             <el-form :model="formData" :rules="rules" label-width="80px" label-position="right" ref="form">
@@ -102,8 +102,12 @@ export default {
                         required: true,
                         required: true,
                         message: '请输入验证码',
                         message: '请输入验证码',
                         trigger: 'blur'
                         trigger: 'blur'
-                    } /* ,
-                    { validator: validateCode, trigger: 'blur' } */
+                    },
+                    {
+                        pattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                        message: '密码必须包含大小写字母和数字,且长度为8-30',
+                        trigger: 'blur'
+                    }
                 ],
                 ],
                 passwordagain: [
                 passwordagain: [
                     { required: true, message: '请输入确认密码', trigger: 'blur' },
                     { required: true, message: '请输入确认密码', trigger: 'blur' },

+ 10 - 2
src/main/vue/src/plugins/http.js

@@ -105,17 +105,25 @@ export default {
                 options = options || {};
                 options = options || {};
                 options.headers = options.headers || {};
                 options.headers = options.headers || {};
                 body = body || {};
                 body = body || {};
-                if (!(body instanceof FormData)) {
+                if (body instanceof FormData) {
+                    //
+                } else {
+                    options.headers['Content-Encrypted'] = 'true';
                     if (options.body === 'json') {
                     if (options.body === 'json') {
                         body = encrypt(JSON.stringify(body));
                         body = encrypt(JSON.stringify(body));
                         options.headers['content-type'] = 'application/json';
                         options.headers['content-type'] = 'application/json';
                     } else {
                     } else {
+                        for (let key of Object.keys(body)) {
+                            if (body[key] != undefined && body[key] !== '') {
+                                body[key] = encrypt(body[key] + '');
+                            }
+                        }
                         body = qs.stringify(body);
                         body = qs.stringify(body);
                     }
                     }
                 }
                 }
                 return new Promise((resolve, reject) => {
                 return new Promise((resolve, reject) => {
                     axiosInstance
                     axiosInstance
-                        .post(url, body, { withCredentials: true })
+                        .post(url, body, { withCredentials: true, headers: { ...options.headers } })
                         .then(res => {
                         .then(res => {
                             resolve(res.data);
                             resolve(res.data);
                         })
                         })

+ 152 - 6
src/main/vue/src/views/Admin.vue

@@ -71,7 +71,7 @@
                     :unique-opened="true"
                     :unique-opened="true"
                     :router="true"
                     :router="true"
                     :default-active="activeMenu"
                     :default-active="activeMenu"
-                    style="border-right: 1px solid #545c64;"
+                    style="border-right: 1px solid #545c64"
                     class="el-menu-vertical-demo"
                     class="el-menu-vertical-demo"
                 >
                 >
                     <sys-menu v-for="item in menus" :menu="item" :key="item.id"> </sys-menu>
                     <sys-menu v-for="item in menus" :menu="item" :key="item.id"> </sys-menu>
@@ -82,7 +82,7 @@
             <el-header class="header">
             <el-header class="header">
                 <div class="header-btn" @click="collapse = !collapse">
                 <div class="header-btn" @click="collapse = !collapse">
                     <div :style="{ transform: collapse ? 'rotate(90deg)' : '' }">
                     <div :style="{ transform: collapse ? 'rotate(90deg)' : '' }">
-                        <i class="fas fa-bars" style="font-size: 20px;"></i>
+                        <i class="fas fa-bars" style="font-size: 20px"></i>
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="header-title"></div>
                 <div class="header-title"></div>
@@ -93,13 +93,14 @@
                     :open-delay="1000"
                     :open-delay="1000"
                 >
                 >
                     <div class="header-btn" @click="toggleFullScreen" ref="fullscreen">
                     <div class="header-btn" @click="toggleFullScreen" ref="fullscreen">
-                        <i class="fas fa-expand" style="font-size: 20px;"></i>
+                        <i class="fas fa-expand" style="font-size: 20px"></i>
                     </div>
                     </div>
                 </el-tooltip>
                 </el-tooltip>
 
 
-                <el-dropdown @command="onCommand" style="margin-left: 20px;">
+                <el-dropdown @command="onCommand" style="margin-left: 20px">
                     <img :src="userInfo ? userInfo.avatar || '' : ''" class="avatar" />
                     <img :src="userInfo ? userInfo.avatar || '' : ''" class="avatar" />
                     <el-dropdown-menu slot="dropdown">
                     <el-dropdown-menu slot="dropdown">
+                        <el-dropdown-item command="pwd" style="word-break: keep-all">修改密码 </el-dropdown-item>
                         <el-dropdown-item command="logout">退出登录 </el-dropdown-item>
                         <el-dropdown-item command="logout">退出登录 </el-dropdown-item>
                     </el-dropdown-menu>
                     </el-dropdown-menu>
                 </el-dropdown>
                 </el-dropdown>
@@ -145,13 +146,38 @@
                 <router-view> </router-view>
                 <router-view> </router-view>
             </el-main>
             </el-main>
         </el-container>
         </el-container>
+        <el-dialog
+            :visible.sync="showPwdDialog"
+            title="修改密码"
+            width="500px"
+            top="30vh"
+            :close-on-click-modal="false"
+            custom-class="change-pwd-dialog"
+        >
+            <el-form :model="pwdForm" label-width="100px" label-position="right" ref="pwdForm" :rules="pwdRules">
+                <el-form-item label="输入新密码" prop="password">
+                    <el-input v-model="pwdForm.password" type="password" placeholder="输入新密码"></el-input>
+                </el-form-item>
+                <el-form-item label="确认新密码" prop="repeat">
+                    <el-input v-model="pwdForm.repeat" type="password" placeholder="确认新密码"></el-input>
+                </el-form-item>
+                <el-form-item label="验证码" prop="code">
+                    <el-input v-model="pwdForm.code" placeholder="请输入验证码" style="width: 150px"></el-input>
+                    <img :src="captcha" class="captcha-image" @click="refreshCaptcha" />
+                </el-form-item>
+            </el-form>
+            <span slot="footer">
+                <el-button @click="showPwdDialog = false">取 消</el-button>
+                <el-button type="primary" @click="savePwd" :loading="pwdLoading">确认修改</el-button>
+            </span>
+        </el-dialog>
     </el-container>
     </el-container>
 </template>
 </template>
 
 
 <script>
 <script>
 import SysMenu from '../components/SysMenu';
 import SysMenu from '../components/SysMenu';
 import { mapState } from 'vuex';
 import { mapState } from 'vuex';
-
+import { isBefore, parse, format, addDays } from 'date-fns';
 export default {
 export default {
     name: 'admin',
     name: 'admin',
     created() {
     created() {
@@ -198,6 +224,13 @@ export default {
         }
         }
         this.currentTab = this.$route.name;
         this.currentTab = this.$route.name;
         this.getMenus();
         this.getMenus();
+
+        let pwdUpdateAt = this.userInfo.pwdUpdateAt;
+        if (!pwdUpdateAt || isBefore(parse(pwdUpdateAt, 'yyyy-MM-dd HH:mm:ss', new Date()), addDays(new Date(), -30))) {
+            this.$alert('您已30天未修改密码,请尽快修改密码', '提示').then(() => {
+                this.onShowPwdDialog();
+            });
+        }
     },
     },
     data() {
     data() {
         return {
         return {
@@ -217,7 +250,62 @@ export default {
             ],
             ],
             currentTab: 'roomStatus',
             currentTab: 'roomStatus',
             lastX: 0,
             lastX: 0,
-            leftMouseDown: false
+            leftMouseDown: false,
+            showPwdDialog: false,
+            pwdForm: {
+                password: '',
+                repeat: '',
+                code: '',
+                key: ''
+            },
+            captcha: '',
+            pwdRules: {
+                password: [
+                    { required: true, message: '请输入新密码', trigger: 'blur' },
+                    {
+                        pattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                        message: '密码必须包含大小写字母和数字,且长度为8-30',
+                        trigger: 'blur'
+                    }
+                ],
+                repeat: [
+                    { required: true, message: '请确认新密码', trigger: 'blur' },
+                    {
+                        validator: (rule, value, callback) => {
+                            if (value && value == this.pwdForm.password) {
+                                callback();
+                            } else {
+                                callback(new Error('两次密码输入不一致'));
+                            }
+                        },
+                        trigger: 'blur'
+                    }
+                ],
+                code: [
+                    { required: true, message: '请输入验证码', trigger: 'blur' },
+                    {
+                        validator: (rule, value, callback) => {
+                            this.$http
+                                .get('/captcha/verify', {
+                                    key: this.pwdForm.key,
+                                    code: value
+                                })
+                                .then(res => {
+                                    if (res === true) {
+                                        callback();
+                                    } else {
+                                        callback(new Error('验证码错误'));
+                                    }
+                                })
+                                .catch(e => {
+                                    callback(new Error('验证码错误'));
+                                });
+                        },
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            pwdLoading: false
         };
         };
     },
     },
     computed: {
     computed: {
@@ -279,8 +367,55 @@ export default {
                 localStorage.removeItem('token');
                 localStorage.removeItem('token');
                 this.$store.commit('updateUserInfo', null);
                 this.$store.commit('updateUserInfo', null);
                 this.$router.replace('/login');
                 this.$router.replace('/login');
+            } else if (command === 'pwd') {
+                this.onShowPwdDialog();
             }
             }
         },
         },
+        onShowPwdDialog() {
+            this.$refs.pwdForm && this.$refs.pwdForm.resetFields();
+            this.$http.get('/captcha/get').then(res => {
+                this.captcha = res.image;
+                this.pwdForm = {
+                    password: '',
+                    repeat: '',
+                    code: '',
+                    key: res.key
+                };
+                this.showPwdDialog = true;
+            });
+        },
+        savePwd() {
+            this.pwdLoading = true;
+            this.$refs.pwdForm.validate(valid => {
+                if (valid) {
+                    this.$http
+                        .post('/user/changePassword', {
+                            ...this.pwdForm
+                        })
+                        .then(res => {
+                            console.log(res);
+                            this.pwdLoading = false;
+                            this.showPwdDialog = false;
+                            localStorage.removeItem('token');
+                            this.$store.commit('updateUserInfo', null);
+                            this.$router.replace('/login');
+                            this.$message.success('修改成功,请重新登录');
+                        })
+                        .catch(e => {
+                            this.pwdLoading = false;
+                            this.$message.error(e.error || '修改失败');
+                        });
+                } else {
+                    this.pwdLoading = false;
+                }
+            });
+        },
+        refreshCaptcha() {
+            this.$http.get('/captcha/get').then(res => {
+                this.captcha = res.image;
+                this.pwdForm.key = res.key;
+            });
+        },
         removeTab(removeIndex) {
         removeTab(removeIndex) {
             let currentIndex = this.tabs.findIndex(i => i.name === this.$route.name);
             let currentIndex = this.tabs.findIndex(i => i.name === this.$route.name);
             if (this.tabs.length > 1 && removeIndex > 0) {
             if (this.tabs.length > 1 && removeIndex > 0) {
@@ -478,4 +613,15 @@ export default {
         }
         }
     }
     }
 }
 }
+/deep/ .change-pwd-dialog {
+    .captcha-image {
+        height: 32px;
+        vertical-align: middle;
+        margin-left: 10px;
+        cursor: pointer;
+    }
+    .el-dialog__body {
+        padding-bottom: 0;
+    }
+}
 </style>
 </style>

+ 82 - 28
src/main/vue/src/views/Login.vue

@@ -2,21 +2,24 @@
     <div class="container" :style="{ backgroundImage: 'url(' + require('../assets/bg_login.jpg') + ')' }">
     <div class="container" :style="{ backgroundImage: 'url(' + require('../assets/bg_login.jpg') + ')' }">
         <transition :name="`slide-${register ? 'in' : 'out'}`">
         <transition :name="`slide-${register ? 'in' : 'out'}`">
             <div class="login-wrapper" @keyup.enter="doRegister" v-if="register" key="register">
             <div class="login-wrapper" @keyup.enter="doRegister" v-if="register" key="register">
-                <el-page-header @back="register = false" title="登录" style="width: 350px; line-height: 60px;">
+                <el-page-header @back="register = false" title="登录" style="width: 350px; line-height: 60px">
                     <div class="register-title" slot="content">注册账号</div>
                     <div class="register-title" slot="content">注册账号</div>
                 </el-page-header>
                 </el-page-header>
 
 
-                <el-form :model="registerInfo" style="width: 350px;" ref="registerForm">
-                    <el-form-item prop="username" :rules="{ required: true, message: '请输入用户名', trigger: 'blur' }">
+                <el-form :model="registerInfo" style="width: 350px" ref="registerForm" :rules="registerRules">
+                    <el-form-item prop="username">
                         <el-input v-model="registerInfo.username" placeholder="用户名"> </el-input>
                         <el-input v-model="registerInfo.username" placeholder="用户名"> </el-input>
                     </el-form-item>
                     </el-form-item>
-                    <el-form-item prop="password" :rules="{ required: true, message: '请输入密码', trigger: 'blur' }">
+                    <el-form-item prop="password">
                         <el-input v-model="registerInfo.password" placeholder="密码" type="password"></el-input>
                         <el-input v-model="registerInfo.password" placeholder="密码" type="password"></el-input>
                     </el-form-item>
                     </el-form-item>
+                    <el-form-item prop="password1">
+                        <el-input v-model="registerInfo.password1" placeholder="重复密码" type="password"></el-input>
+                    </el-form-item>
                     <el-form-item>
                     <el-form-item>
-                        <el-button :loading="loading" @click="doRegister" type="primary" style="width: 100%;"
-                            >注册</el-button
-                        >
+                        <el-button :loading="loading" @click="doRegister" type="primary" style="width: 100%">
+                            注册
+                        </el-button>
                     </el-form-item>
                     </el-form-item>
                 </el-form>
                 </el-form>
             </div>
             </div>
@@ -24,21 +27,15 @@
                 <div class="title">欢迎登录</div>
                 <div class="title">欢迎登录</div>
                 <el-tabs v-model="activeName" stretch class="tab-list">
                 <el-tabs v-model="activeName" stretch class="tab-list">
                     <el-tab-pane label="用户名登陆" name="first">
                     <el-tab-pane label="用户名登陆" name="first">
-                        <el-form :model="userInfo" style="width: 350px;" ref="form">
-                            <el-form-item
-                                prop="username"
-                                :rules="{ required: true, message: '请输入用户名', trigger: 'blur' }"
-                            >
+                        <el-form :model="userInfo" style="width: 350px" ref="form" :rules="loginRules">
+                            <el-form-item prop="username">
                                 <el-input v-model="userInfo.username" placeholder="用户名"> </el-input>
                                 <el-input v-model="userInfo.username" placeholder="用户名"> </el-input>
                             </el-form-item>
                             </el-form-item>
-                            <el-form-item
-                                prop="password"
-                                :rules="{ required: true, message: '请输入密码', trigger: 'blur' }"
-                            >
+                            <el-form-item prop="password">
                                 <el-input v-model="userInfo.password" placeholder="密码" type="password"></el-input>
                                 <el-input v-model="userInfo.password" placeholder="密码" type="password"></el-input>
                             </el-form-item>
                             </el-form-item>
                             <el-form-item>
                             <el-form-item>
-                                <el-button :loading="loading" @click="login" type="primary" style="width: 100%;"
+                                <el-button :loading="loading" @click="login" type="primary" style="width: 100%"
                                     >登录
                                     >登录
                                 </el-button>
                                 </el-button>
                             </el-form-item>
                             </el-form-item>
@@ -48,15 +45,15 @@
                                     style="width: 100%;">手机登录</el-button>
                                     style="width: 100%;">手机登录</el-button>
                             </el-form-item> -->
                             </el-form-item> -->
                             <el-form-item label="">
                             <el-form-item label="">
-                                <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox>
-                                <el-button type="text" style="float: right;" @click="register = true"
+                                <!-- <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox> -->
+                                <el-button type="text" style="float: right" @click="register = true"
                                     >注册账号
                                     >注册账号
                                 </el-button>
                                 </el-button>
                             </el-form-item>
                             </el-form-item>
                         </el-form>
                         </el-form>
                     </el-tab-pane>
                     </el-tab-pane>
-                    <el-tab-pane label="验证码登陆" name="second">
-                        <el-form :model="userInfo" style="width: 350px;" ref="form2">
+                    <el-tab-pane label="验证码登陆" name="second" disabled>
+                        <el-form :model="userInfo" style="width: 350px" ref="form2">
                             <el-form-item
                             <el-form-item
                                 prop="phone"
                                 prop="phone"
                                 :rules="{ required: true, message: '请输入手机号', trigger: 'blur' }"
                                 :rules="{ required: true, message: '请输入手机号', trigger: 'blur' }"
@@ -83,13 +80,13 @@
                                 </el-button>
                                 </el-button>
                             </el-form-item> -->
                             </el-form-item> -->
                             <el-form-item>
                             <el-form-item>
-                                <el-button :loading="loading" @click="phonelogin" type="primary" style="width: 100%;"
+                                <el-button :loading="loading" @click="phonelogin" type="primary" style="width: 100%"
                                     >手机登录</el-button
                                     >手机登录</el-button
                                 >
                                 >
                             </el-form-item>
                             </el-form-item>
                             <el-form-item label="">
                             <el-form-item label="">
                                 <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox>
                                 <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox>
-                                <el-button type="text" style="float: right;" @click="register = true"
+                                <el-button type="text" style="float: right" @click="register = true"
                                     >注册账号
                                     >注册账号
                                 </el-button>
                                 </el-button>
                             </el-form-item>
                             </el-form-item>
@@ -101,6 +98,7 @@
     </div>
     </div>
 </template>
 </template>
 <script>
 <script>
+import { parse, format, isBefore, addMinutes } from 'date-fns';
 export default {
 export default {
     data() {
     data() {
         return {
         return {
@@ -119,12 +117,66 @@ export default {
             },
             },
             activeName: 'first',
             activeName: 'first',
             time: 0,
             time: 0,
-            sending: false
+            sending: false,
+            registerRules: {
+                username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+                password: [
+                    { required: true, message: '请输入密码', trigger: 'blur' },
+                    {
+                        pattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                        message: '密码必须包含大小写字母和数字,且长度为8-30',
+                        trigger: 'blur'
+                    }
+                ],
+                password1: [
+                    {
+                        validator: (rule, value, callback) => {
+                            if (value !== this.registerInfo.password) {
+                                callback(new Error('两次输入密码不一致!'));
+                            } else {
+                                callback();
+                            }
+                        }
+                    }
+                ]
+            },
+            loginRules: {
+                username: [
+                    { required: true, message: '请输入用户名', trigger: 'blur' },
+                    {
+                        validator: (rule, value, callback) => {
+                            if (
+                                value !== '' &&
+                                /[`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%&*()\-+={}|《》?:“”【】、;‘’,。、]/im.test(
+                                    value
+                                )
+                            ) {
+                                callback(new Error('用户名输入不正确'));
+                            } else {
+                                callback();
+                            }
+                        }
+                    }
+                ],
+                password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
+            }
         };
         };
     },
     },
     methods: {
     methods: {
         login() {
         login() {
             this.$refs.form.validate(valid => {
             this.$refs.form.validate(valid => {
+                let tryNum = parseInt(localStorage.getItem('loginTry::' + this.userInfo.username) || 0);
+
+                let lastLogin = localStorage.getItem('lastLogin');
+                lastLogin = parse(lastLogin, 'yyyy-MM-dd HH:mm:ss', new Date());
+                if (isBefore(lastLogin, addMinutes(new Date(), -30))) {
+                    tryNum = 0;
+                } else if (tryNum >= 5) {
+                    this.$message.error('密码错误超过5次,账号已锁定');
+                    return;
+                }
+                localStorage.setItem('loginTry::' + this.userInfo.username, ++tryNum);
+                localStorage.setItem('lastLogin', format(new Date(), 'yyyy-MM-dd HH:mm:ss'));
                 if (valid) {
                 if (valid) {
                     this.loading = true;
                     this.loading = true;
                     this.$http
                     this.$http
@@ -139,18 +191,20 @@ export default {
                         })
                         })
                         .then(res => {
                         .then(res => {
                             this.loading = false;
                             this.loading = false;
-                            if (res.authorities.find(i => i.name === 'ROLE_ADMIN')) {
-                                this.$store.commit('updateIsAdmin', true);
-                            }
                             this.$store.commit('updateUserInfo', res);
                             this.$store.commit('updateUserInfo', res);
                             this.$router.replace({
                             this.$router.replace({
                                 name: this.$route.params.name || 'audit'
                                 name: this.$route.params.name || 'audit'
                             });
                             });
+                            localStorage.removeItem('loginTry::' + this.userInfo.username);
                         })
                         })
                         .catch(e => {
                         .catch(e => {
                             console.log(e);
                             console.log(e);
                             this.loading = false;
                             this.loading = false;
-                            this.$message.error(e.error);
+                            if (tryNum >= 5) {
+                                this.$message.error('密码错误超过5次,账号已锁定');
+                            } else {
+                                this.$message.error(e.error + ',剩余尝试次数' + (5 - tryNum));
+                            }
                         });
                         });
                 }
                 }
             });
             });

+ 16 - 3
src/main/vue/src/views/UserEdit.vue

@@ -6,7 +6,7 @@
             ref="form"
             ref="form"
             label-width="80px"
             label-width="80px"
             label-position="right"
             label-position="right"
-            style="max-width: 500px;"
+            style="max-width: 500px"
         >
         >
             <el-form-item prop="avatar" label="头像">
             <el-form-item prop="avatar" label="头像">
                 <crop-upload v-model="formData.avatar"></crop-upload>
                 <crop-upload v-model="formData.avatar"></crop-upload>
@@ -85,7 +85,14 @@ export default {
                 ],
                 ],
                 username: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
                 username: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
                 nickname: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
                 nickname: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
-                password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
+                password: [
+                    { required: true, message: '请输入密码', trigger: 'blur' },
+                    {
+                        pattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                        message: '密码必须包含大小写字母和数字,且长度为8-30',
+                        trigger: 'blur'
+                    }
+                ],
                 phone: [
                 phone: [
                     {
                     {
                         regexp: /^1[3-9]\d{9}$/,
                         regexp: /^1[3-9]\d{9}$/,
@@ -150,7 +157,13 @@ export default {
                 .catch(() => {});
                 .catch(() => {});
         },
         },
         resetPassword() {
         resetPassword() {
-            this.$prompt('请输入新密码', '重置密码', { inputType: 'password' })
+            this.$prompt('请输入新密码', '重置密码', {
+                inputType: 'password',
+                inputPattern: /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]).{8,30}/,
+                inputErrorMessage: '密码必须包含大小写字母和数字,且长度为8-30',
+                closeOnClickModal: false,
+                closeOnPressEscape: false
+            })
                 .then(res => {
                 .then(res => {
                     console.log(res);
                     console.log(res);
                     if (res.value) {
                     if (res.value) {