用户登录问题修改
This commit is contained in:
parent
312c9ebaec
commit
eb9e9db8b1
|
|
@ -25,6 +25,18 @@ export function isLogin(data) {
|
|||
})
|
||||
}
|
||||
|
||||
export function isAdmin(data) {
|
||||
return request({
|
||||
url: '/auth/isAdmin',
|
||||
headers: {
|
||||
isToken: false,
|
||||
repeatSubmit: false
|
||||
},
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function getPhoneCode(payload) {
|
||||
return request({
|
||||
url: '/auth/getPhoneCode',
|
||||
|
|
|
|||
|
|
@ -145,3 +145,12 @@ export function approvalStatus(data) {
|
|||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
//用户注册审批
|
||||
export function checkPasswordStatus(data) {
|
||||
return request({
|
||||
url: '/system/user/checkPasswordStatus',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,29 @@
|
|||
<settings/>
|
||||
</right-panel>
|
||||
</div>
|
||||
<el-dialog :title="title" :visible.sync="showChangePasswordDialog" width="30%" :close-on-click-modal="false"
|
||||
:show-close="false"
|
||||
>
|
||||
<el-form ref="form" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="旧密码" prop="oldPassword">
|
||||
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password/>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password/>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item>
|
||||
<el-button type="primary" size="mini" @click="submit">保存</el-button>
|
||||
<el-button type="danger" size="mini" @click="close">关闭</el-button>
|
||||
</el-form-item>-->
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submit">确 定</el-button>
|
||||
<el-button @click="close">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -21,9 +44,43 @@ import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
|
|||
import ResizeMixin from './mixin/ResizeHandler'
|
||||
import { mapState } from 'vuex'
|
||||
import variables from '@/assets/styles/variables.scss'
|
||||
import { validateNewPassword } from '@/utils/validate'
|
||||
import { updateUserPwd, checkPasswordStatus } from '@/api/system/user'
|
||||
|
||||
export default {
|
||||
name: 'Layout',
|
||||
data() {
|
||||
const equalToPassword = (rule, value, callback) => {
|
||||
if (this.user.newPassword !== value) {
|
||||
callback(new Error('两次输入的密码不一致'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
showChangePasswordDialog: false, // 控制弹窗显示
|
||||
title: '',
|
||||
user: {
|
||||
oldPassword: undefined,
|
||||
newPassword: undefined,
|
||||
confirmPassword: undefined
|
||||
},
|
||||
// 表单校验规则
|
||||
rules: {
|
||||
oldPassword: [
|
||||
{ required: true, message: '旧密码不能为空', trigger: 'blur' }
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: '新密码不能为空', trigger: 'blur' },
|
||||
{ validator: validateNewPassword, trigger: 'blur' }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
|
||||
{ required: true, validator: equalToPassword, trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AppMain,
|
||||
Navbar,
|
||||
|
|
@ -51,22 +108,48 @@ export default {
|
|||
}
|
||||
},
|
||||
variables() {
|
||||
return variables;
|
||||
return variables
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.checkPasswordStatus()
|
||||
},
|
||||
methods: {
|
||||
checkPasswordStatus() {
|
||||
checkPasswordStatus().then(response => {
|
||||
if (response.code === 200) {
|
||||
this.showChangePasswordDialog = response.data
|
||||
this.title = response.msg
|
||||
}
|
||||
})
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
||||
},
|
||||
submit() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => {
|
||||
this.showChangePasswordDialog = false
|
||||
this.$modal.msgSuccess('修改成功')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
close() {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
location.href = '/index';
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/styles/mixin.scss";
|
||||
@import "~@/assets/styles/variables.scss";
|
||||
@import "~@/assets/styles/mixin.scss";
|
||||
@import "~@/assets/styles/variables.scss";
|
||||
|
||||
.app-wrapper {
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
|
@ -76,9 +159,9 @@ export default {
|
|||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
|
|
@ -86,26 +169,26 @@ export default {
|
|||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$base-sidebar-width});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebarHide .fixed-header {
|
||||
.sidebarHide .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile .fixed-header {
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,35 @@
|
|||
import { login, logout, getInfo, refreshToken, getPhoneCode, isLogin } from '@/api/login'
|
||||
import { login, logout, getInfo, refreshToken, getPhoneCode, isLogin,isAdmin } from '@/api/login'
|
||||
import { getToken, setToken, setExpiresIn, removeToken } from '@/utils/auth'
|
||||
|
||||
// 更严格的手机号和邮箱正则表达式
|
||||
const phonePattern = /^(\+86)?1[3-9]\d{9}$/ // 支持前缀 +86
|
||||
const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||
|
||||
// 构建 payload 函数
|
||||
const buildPayload = ({ loginMethod, username, password, uuid, code, mobile, verificationCode }) => {
|
||||
let loginType = ''
|
||||
if (loginMethod === 'mobile') {
|
||||
loginType = phonePattern.test(mobile.trim()) ? 'PHONE_OTP' : emailPattern.test(mobile.trim()) ? 'EMAIL_OTP' : 'PHONE_OTP'
|
||||
return {
|
||||
username: mobile.trim(),
|
||||
verificationCode,
|
||||
uuid,
|
||||
code,
|
||||
loginType
|
||||
}
|
||||
} else {
|
||||
loginType = phonePattern.test(username.trim()) ? 'PHONE_PASSWORD' : emailPattern.test(username.trim()) ? 'EMAIL_PASSWORD' : 'USERNAME_PASSWORD'
|
||||
return {
|
||||
username: username.trim(),
|
||||
password,
|
||||
verificationCode,
|
||||
uuid,
|
||||
code,
|
||||
loginType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const user = {
|
||||
state: {
|
||||
token: getToken(),
|
||||
|
|
@ -12,159 +41,110 @@ const user = {
|
|||
},
|
||||
|
||||
mutations: {
|
||||
SET_TOKEN: (state, token) => {
|
||||
SET_TOKEN(state, token) {
|
||||
state.token = token
|
||||
},
|
||||
SET_EXPIRES_IN: (state, time) => {
|
||||
SET_EXPIRES_IN(state, time) {
|
||||
state.expires_in = time
|
||||
},
|
||||
SET_ID: (state, id) => {
|
||||
SET_ID(state, id) {
|
||||
state.id = id
|
||||
},
|
||||
SET_NAME: (state, name) => {
|
||||
SET_NAME(state, name) {
|
||||
state.name = name
|
||||
},
|
||||
SET_AVATAR: (state, avatar) => {
|
||||
SET_AVATAR(state, avatar) {
|
||||
state.avatar = avatar
|
||||
},
|
||||
SET_ROLES: (state, roles) => {
|
||||
SET_ROLES(state, roles) {
|
||||
state.roles = roles
|
||||
},
|
||||
SET_PERMISSIONS: (state, permissions) => {
|
||||
SET_PERMISSIONS(state, permissions) {
|
||||
state.permissions = permissions
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
IsLogin({ commit }, userInfo) {
|
||||
let payload = {}
|
||||
const loginType = userInfo.loginType
|
||||
if (loginType === 'password') {
|
||||
payload = {
|
||||
username: userInfo.username.trim(),
|
||||
password: userInfo.password,
|
||||
uuid: userInfo.uuid,
|
||||
code: userInfo.code,
|
||||
loginType: loginType
|
||||
}
|
||||
} else if (loginType === 'mobile') {
|
||||
payload = {
|
||||
mobile: userInfo.mobile.trim(),
|
||||
verificationCode: userInfo.verificationCode,
|
||||
uuid: userInfo.uuid,
|
||||
code: userInfo.code,
|
||||
loginType: loginType
|
||||
}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
isLogin(payload).then(res => {
|
||||
resolve(res);
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
const payload = buildPayload(userInfo)
|
||||
return isLogin(payload)
|
||||
.then(res => res)
|
||||
.catch(error => Promise.reject(error))
|
||||
},
|
||||
IsAdmin({ commit }, userInfo) {
|
||||
const payload = buildPayload(userInfo)
|
||||
return isAdmin(payload)
|
||||
.then(res => res)
|
||||
.catch(error => Promise.reject(error))
|
||||
},
|
||||
|
||||
// 登录
|
||||
Login({ commit }, userInfo) {
|
||||
let payload = {}
|
||||
const loginType = userInfo.loginType
|
||||
if (loginType === 'password') {
|
||||
payload = {
|
||||
username: userInfo.username.trim(),
|
||||
password: userInfo.password,
|
||||
uuid: userInfo.uuid,
|
||||
code: userInfo.code,
|
||||
loginType: loginType
|
||||
}
|
||||
} else if (loginType === 'mobile') {
|
||||
payload = {
|
||||
mobile: userInfo.mobile.trim(),
|
||||
verificationCode: userInfo.verificationCode,
|
||||
uuid: userInfo.uuid,
|
||||
code: userInfo.code,
|
||||
loginType: loginType
|
||||
}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
login(payload).then(res => {
|
||||
let data = res.data
|
||||
setToken(data.access_token)
|
||||
commit('SET_TOKEN', data.access_token)
|
||||
setExpiresIn(data.expires_in)
|
||||
commit('SET_EXPIRES_IN', data.expires_in)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
const payload = buildPayload(userInfo)
|
||||
return login(payload)
|
||||
.then(res => {
|
||||
const { access_token, expires_in } = res.data
|
||||
setToken(access_token)
|
||||
commit('SET_TOKEN', access_token)
|
||||
setExpiresIn(expires_in)
|
||||
commit('SET_EXPIRES_IN', expires_in)
|
||||
})
|
||||
.catch(error => Promise.reject(error))
|
||||
},
|
||||
|
||||
// 获取手机验证码
|
||||
GetPhoneCode({ commit }, userInfo) {
|
||||
let payload = {}
|
||||
payload = {
|
||||
const payload = {
|
||||
mobile: userInfo.mobile.trim(),
|
||||
uuid: userInfo.uuid,
|
||||
code: userInfo.code,
|
||||
mobileCodeType: userInfo.mobileCodeType
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
getPhoneCode(payload).then(res => {
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
return getPhoneCode(payload)
|
||||
.then(res => res)
|
||||
.catch(error => Promise.reject(error))
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
GetInfo({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getInfo().then(res => {
|
||||
GetInfo({ commit }) {
|
||||
return getInfo()
|
||||
.then(res => {
|
||||
const user = res.user
|
||||
const avatar = (user.avatar == '' || user.avatar == null) ? require('@/assets/images/profile.jpg') : user.avatar
|
||||
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||
commit('SET_ROLES', res.roles)
|
||||
const avatar = user.avatar ? user.avatar : require('@/assets/images/profile.jpg')
|
||||
commit('SET_ROLES', res.roles && res.roles.length > 0 ? res.roles : ['ROLE_DEFAULT'])
|
||||
commit('SET_PERMISSIONS', res.permissions)
|
||||
} else {
|
||||
commit('SET_ROLES', ['ROLE_DEFAULT'])
|
||||
}
|
||||
commit('SET_ID', user.userId)
|
||||
commit('SET_NAME', user.userName)
|
||||
commit('SET_AVATAR', avatar)
|
||||
resolve(res)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
return res
|
||||
})
|
||||
.catch(error => Promise.reject(error))
|
||||
},
|
||||
|
||||
// 刷新token
|
||||
// 刷新 token
|
||||
RefreshToken({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
refreshToken(state.token).then(res => {
|
||||
setExpiresIn(res.data)
|
||||
commit('SET_EXPIRES_IN', res.data)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
return refreshToken(state.token)
|
||||
.then(res => {
|
||||
const expiresIn = res.data
|
||||
setExpiresIn(expiresIn)
|
||||
commit('SET_EXPIRES_IN', expiresIn)
|
||||
})
|
||||
.catch(error => Promise.reject(error))
|
||||
},
|
||||
|
||||
// 退出系统
|
||||
// 退出登录
|
||||
LogOut({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout(state.token).then(() => {
|
||||
return logout(state.token)
|
||||
.then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
commit('SET_PERMISSIONS', [])
|
||||
removeToken()
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
.catch(error => Promise.reject(error))
|
||||
},
|
||||
|
||||
// 前端 登出
|
||||
// 前端退出
|
||||
FedLogOut({ commit }) {
|
||||
return new Promise(resolve => {
|
||||
commit('SET_TOKEN', '')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// src/config/passwordConfig.js
|
||||
export default {
|
||||
minLength: 8, // 密码最小长度
|
||||
maxLength: 16, // 密码最大长度
|
||||
requireUpperCase: true, // 是否需要大写字母
|
||||
requireLowerCase: true, // 是否需要小写字母
|
||||
requireDigit: true, // 是否需要数字
|
||||
requireSpecialChar: true, // 是否需要特殊字符
|
||||
weakPasswords: ['123456', 'password', 'qwerty'], // 弱密码列表
|
||||
restrictConsecutiveChars: true, // 是否限制连续字符
|
||||
maxConsecutiveChars: 3, // 最大连续字符数
|
||||
excludeUsernameInPassword: true, // 是否不允许密码包含用户名
|
||||
passwordHistoryLimit: 5 // 历史密码限制条数
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import {CONFIG} from '@/utils/configure'
|
||||
import { CONFIG } from '@/utils/configure'
|
||||
import passwordConfig from '@/utils/passwordConfig'
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
|
|
@ -149,3 +150,62 @@ export function validatePassword(rule, value, callback) {
|
|||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
export function validateNewPassword(rule, value, callback) {
|
||||
// 使用配置文件中的策略进行验证
|
||||
|
||||
// 1. 检查密码长度
|
||||
if (value.length < passwordConfig.minLength || value.length > passwordConfig.maxLength) {
|
||||
callback(new Error('密码长度应为' + passwordConfig.minLength + '至' + passwordConfig.maxLength + '位!'))
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 检查密码复杂度
|
||||
const hasUpperCase = /[A-Z]/.test(value)
|
||||
const hasLowerCase = /[a-z]/.test(value)
|
||||
const hasDigit = /\d/.test(value)
|
||||
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value)
|
||||
|
||||
if (passwordConfig.requireUpperCase && !hasUpperCase) {
|
||||
callback(new Error('密码必须包含大写字母!'))
|
||||
return
|
||||
}
|
||||
if (passwordConfig.requireLowerCase && !hasLowerCase) {
|
||||
callback(new Error('密码必须包含小写字母!'))
|
||||
return
|
||||
}
|
||||
if (passwordConfig.requireDigit && !hasDigit) {
|
||||
callback(new Error('密码必须包含数字!'))
|
||||
return
|
||||
}
|
||||
if (passwordConfig.requireSpecialChar && !hasSpecialChar) {
|
||||
callback(new Error('密码必须包含特殊字符!'))
|
||||
return
|
||||
}
|
||||
// 3. 检查是否包含弱密码
|
||||
for (const weakPwd of passwordConfig.weakPasswords) {
|
||||
if (value.includes(weakPwd)) {
|
||||
callback(new Error(`密码包含常见的弱密码片段: ${weakPwd}`))
|
||||
return
|
||||
}
|
||||
}
|
||||
// 4. 检查是否包含超过规定数量的连续字符
|
||||
if (passwordConfig.restrictConsecutiveChars && containsConsecutiveCharacters(value, passwordConfig.maxConsecutiveChars)) {
|
||||
callback(new Error(`密码不能包含超过${passwordConfig.maxConsecutiveChars}位连续字符!`))
|
||||
return
|
||||
}
|
||||
callback() // 验证成功
|
||||
}
|
||||
|
||||
function containsConsecutiveCharacters(password, maxConsecutive) {
|
||||
let count = 1
|
||||
for (let i = 1; i < password.length; i++) {
|
||||
if (password[i] === password[i - 1]) {
|
||||
count++
|
||||
if (count > maxConsecutive) return true
|
||||
} else {
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<b>当前版本:</b> <span>v{{ version }}</span>
|
||||
</p>
|
||||
</el-col>
|
||||
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 50px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
|
|
@ -57,9 +56,6 @@
|
|||
>http://www.ahbonus.cn</el-link
|
||||
>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
|
@ -91,6 +87,8 @@
|
|||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,21 @@
|
|||
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="isAdmin" prop="verificationCode">
|
||||
<el-input v-model="loginForm.verificationCode" placeholder="请输入验证码">
|
||||
<template slot="append">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="sendAdminCode"
|
||||
:disabled="isSendingCode || captchaEnabled?!loginForm.code:false"
|
||||
class="send-code-button"
|
||||
>
|
||||
{{ countdown === 0 ? '获取验证码' : `${countdown}s` }}
|
||||
</el-button>
|
||||
</template>
|
||||
<svg-icon slot="prefix" icon-class="message" class="el-input__icon input-icon"/>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-form-item prop="mobile">
|
||||
|
|
@ -92,12 +107,22 @@
|
|||
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
|
||||
</div>
|
||||
<el-form-item style="width: 100%;">
|
||||
<el-button
|
||||
<el-button v-if="!isAdmin"
|
||||
:loading="loading"
|
||||
size="medium"
|
||||
type="primary"
|
||||
style="width: 100%;"
|
||||
@click.native.prevent="isLogin"
|
||||
@click="loginMethod === 'password' ?IsAdmin():isLogin()"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
</el-button>
|
||||
<el-button v-if="isAdmin"
|
||||
:loading="loading"
|
||||
size="medium"
|
||||
type="primary"
|
||||
style="width: 100%;"
|
||||
@click="isLogin()"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
|
|
@ -140,6 +165,7 @@ export default {
|
|||
wx: wx,
|
||||
qq: qq,
|
||||
codeUrl: '',
|
||||
isAdmin: false,
|
||||
loginForm: {
|
||||
username: '',
|
||||
password: '',
|
||||
|
|
@ -228,7 +254,7 @@ export default {
|
|||
Cookies.remove('password')
|
||||
Cookies.remove('rememberMe')
|
||||
}
|
||||
this.loginForm.loginType = this.loginMethod
|
||||
this.loginForm.loginMethod = this.loginMethod
|
||||
this.$store.dispatch('Login', this.loginForm)
|
||||
.then(() => this.$router.push({ path: this.redirect || '/' }))
|
||||
.catch(() => {
|
||||
|
|
@ -238,6 +264,17 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
IsAdmin() {
|
||||
this.loginForm.loginMethod = this.loginMethod
|
||||
this.$store.dispatch('IsAdmin', this.loginForm).then(res => {
|
||||
if (res.data) {
|
||||
this.isAdmin = res.data
|
||||
//this.isLogin()
|
||||
} else {
|
||||
this.isLogin()
|
||||
}
|
||||
})
|
||||
},
|
||||
isLogin() {
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (valid) {
|
||||
|
|
@ -252,7 +289,7 @@ export default {
|
|||
Cookies.remove('rememberMe')
|
||||
}
|
||||
let that = this
|
||||
this.loginForm.loginType = this.loginMethod
|
||||
this.loginForm.loginMethod = this.loginMethod
|
||||
this.$store.dispatch('IsLogin', this.loginForm)
|
||||
.then(res => {
|
||||
if (res.data) {
|
||||
|
|
@ -274,12 +311,36 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
sendAdminCode(){
|
||||
this.loginForm.mobile = this.loginForm.username
|
||||
this.$store.dispatch('GetPhoneCode', this.loginForm)
|
||||
.then(() => {
|
||||
this.isSendingCode = true
|
||||
this.countdown = 60
|
||||
const timer = setInterval(() => {
|
||||
this.countdown -= 1
|
||||
if (this.countdown <= 0) {
|
||||
clearInterval(timer)
|
||||
this.isSendingCode = false
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false
|
||||
// 如果启用了验证码,则重新获取验证码
|
||||
if (this.captchaEnabled) {
|
||||
this.getCode()
|
||||
}
|
||||
// 其他失败处理逻辑
|
||||
this.isSendingCode = false
|
||||
this.countdown = 0
|
||||
})
|
||||
},
|
||||
sendCode() {
|
||||
if (this.captchaEnabled ? !this.loginForm.code : false) {
|
||||
this.$message.error('请先填写验证码')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.loginForm.mobile) {
|
||||
this.$message.error('请先填写' + getCodePlaceholderText())
|
||||
return
|
||||
|
|
@ -314,6 +375,7 @@ export default {
|
|||
this.loginForm.code = ''
|
||||
this.loginForm.verificationCode = ''
|
||||
this.loginForm.loginMethod = ''
|
||||
this.isAdmin=false
|
||||
this.getCode()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@
|
|||
|
||||
<script>
|
||||
import { getCodeImg, register } from '@/api/login'
|
||||
import { validatePassword } from '@/utils/validate'
|
||||
import { CONFIG,REGISTER_CONFIG } from '@/utils/configure'
|
||||
import { validateNewPassword, validatePassword } from '@/utils/validate'
|
||||
import { CONFIG, REGISTER_CONFIG } from '@/utils/configure'
|
||||
|
||||
export default {
|
||||
name: 'Register',
|
||||
|
|
@ -142,7 +142,6 @@ export default {
|
|||
nickName: '',
|
||||
code: '',
|
||||
uuid: '',
|
||||
loginType: 'mobile',
|
||||
mobileCodeType: 'register'
|
||||
},
|
||||
registerRules: {
|
||||
|
|
@ -155,7 +154,7 @@ export default {
|
|||
],
|
||||
password: [
|
||||
{ required: true, trigger: 'blur', message: '请输入您的密码' },
|
||||
{ validator: validatePassword, trigger: 'blur' }
|
||||
{ validator: validateNewPassword, trigger: 'blur' }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, trigger: 'blur', message: '请再次输入您的密码' },
|
||||
|
|
|
|||
|
|
@ -305,8 +305,9 @@ import {
|
|||
import { getToken } from '@/utils/auth'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { validPwd, validatePassword } from '@/utils/validate'
|
||||
import { validPwd, validatePassword, validateNewPassword } from '@/utils/validate'
|
||||
import { hashWithSM3AndSalt } from '@/utils/sm'
|
||||
import passwordConfig from '@/utils/passwordConfig'
|
||||
|
||||
export default {
|
||||
name: 'User',
|
||||
|
|
@ -416,7 +417,7 @@ export default {
|
|||
],
|
||||
password: [
|
||||
{ required: true, message: '密码不能为空', trigger: 'blur' },
|
||||
{ validator: validatePassword, trigger: 'blur' }
|
||||
{ validator: validateNewPassword, trigger: 'blur' }
|
||||
],
|
||||
roleIds: [
|
||||
{ required: true, message: '请选择角色', trigger: 'change' }
|
||||
|
|
@ -612,11 +613,13 @@ export default {
|
|||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
closeOnClickModal: false,
|
||||
inputPattern: /^.{8,20}$/,
|
||||
inputErrorMessage: '用户密码长度必须介于 8 和 20 之间',
|
||||
inputPattern: /^.{8,16}$/,
|
||||
inputErrorMessage: '用户密码长度必须介于 8 和 16 之间',
|
||||
inputValidator: (value) => {
|
||||
if (!/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,20}$/.test(value)) {
|
||||
return '密码规则为:至少一个字母,一个数字和一个特殊字符'
|
||||
// 调用 validateNewPassword 校验
|
||||
const errorMessage = this.validateNewPasswordForPrompt(row.userName, value)
|
||||
if (errorMessage) {
|
||||
return errorMessage // 返回错误信息进行提示
|
||||
}
|
||||
}
|
||||
}).then(({ value }) => {
|
||||
|
|
@ -694,6 +697,64 @@ export default {
|
|||
// 提交上传文件
|
||||
submitFileForm() {
|
||||
this.$refs.upload.submit()
|
||||
},
|
||||
validateNewPasswordForPrompt(username, newPassword) {
|
||||
// 1. 检查密码长度
|
||||
if (newPassword.length < passwordConfig.minLength || newPassword.length > passwordConfig.maxLength) {
|
||||
return '密码长度应为' + passwordConfig.minLength + '至' + passwordConfig.maxLength + '位!'
|
||||
}
|
||||
|
||||
// 2. 检查密码复杂度
|
||||
const hasUpperCase = /[A-Z]/.test(newPassword)
|
||||
const hasLowerCase = /[a-z]/.test(newPassword)
|
||||
const hasDigit = /\d/.test(newPassword)
|
||||
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(newPassword)
|
||||
|
||||
if (passwordConfig.requireUpperCase && !hasUpperCase) {
|
||||
return '密码必须包含大写字母!'
|
||||
}
|
||||
if (passwordConfig.requireLowerCase && !hasLowerCase) {
|
||||
return '密码必须包含小写字母!'
|
||||
}
|
||||
if (passwordConfig.requireDigit && !hasDigit) {
|
||||
return '密码必须包含数字!'
|
||||
}
|
||||
if (passwordConfig.requireSpecialChar && !hasSpecialChar) {
|
||||
return '密码必须包含特殊字符!'
|
||||
}
|
||||
|
||||
// 3. 检查是否包含弱密码
|
||||
for (const weakPwd of passwordConfig.weakPasswords) {
|
||||
if (newPassword.includes(weakPwd)) {
|
||||
return `密码包含常见的弱密码片段: ${weakPwd}`
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 检查是否包含超过规定数量的连续字符
|
||||
if (passwordConfig.restrictConsecutiveChars && this.containsConsecutiveCharacters(newPassword, passwordConfig.maxConsecutiveChars)) {
|
||||
return `密码不能包含超过${passwordConfig.maxConsecutiveChars}位连续字符!`
|
||||
}
|
||||
|
||||
// 5. 检查密码中是否包含用户名
|
||||
if (passwordConfig.excludeUsernameInPassword && newPassword.includes(username)) {
|
||||
return '密码不能包含账号!'
|
||||
}
|
||||
|
||||
// 6. 新密码通过所有校验,返回空表示通过校验
|
||||
return null
|
||||
},
|
||||
|
||||
containsConsecutiveCharacters(password, maxConsecutive) {
|
||||
let count = 1
|
||||
for (let i = 1; i < password.length; i++) {
|
||||
if (password[i] === password[i - 1]) {
|
||||
count++
|
||||
if (count > maxConsecutive) return true
|
||||
} else {
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<script>
|
||||
import { updateUserPwd } from "@/api/system/user";
|
||||
import { validatePassword } from '@/utils/validate'
|
||||
import { validateNewPassword, validatePassword } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
@ -42,7 +42,7 @@ export default {
|
|||
],
|
||||
newPassword: [
|
||||
{ required: true, message: "新密码不能为空", trigger: "blur" },
|
||||
{ validator: validatePassword, trigger: 'blur' }
|
||||
{ validator: validateNewPassword, trigger: 'blur' }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: "确认密码不能为空", trigger: "blur" },
|
||||
|
|
|
|||
|
|
@ -40,8 +40,16 @@ module.exports = {
|
|||
pathRewrite: {
|
||||
['^' + process.env.VUE_APP_BASE_API]: ''
|
||||
}
|
||||
},
|
||||
"/api" : {
|
||||
"target" : "http://192.168.0.21:17861",
|
||||
//设置允许跨域——此处我经过测试发现可有可无
|
||||
"changeOrigin" : true,
|
||||
"pathRewrite" : {
|
||||
"^/api" : ""
|
||||
}
|
||||
},
|
||||
},
|
||||
disableHostCheck: true
|
||||
},
|
||||
css: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue