这里写自定义目录标题
前台:Vue
后台:Spring boot
Bcrypt
bcrypt,是一个跨平台的文件加密工具。由它加密的文件可在所有支持的操作系统和处理器上进行转移。它的口令必须是8至56个字符,并将在内部被转化为448位的密钥。(百度百科)
1.需求描述
个人信息中修改个人密码,要输入旧密码,然后输入新密码,再确认密码。
效果
2.问题描述
在做一个Spring项目的时候,因为使用了Spring Security,其中有一项个人信息,个人信息具备修改密码的功能,但是数据库中存储的密码是经过 Bcrypt 加密之后存放的,这样就涉及到以下问题:
- 用户前台修改密码时输入的明文旧密码和数据库中经过加密保存密码比较是否相同。
- 用户输入新密码存放到数据库中需要经过加密
- 用户新密码和确认密码比较是否一致
- 三个密码的验证顺序
3.解决办法
我们先看一下修改密码的流程
一丶填写原密码
二丶填写新密码并填写确认密码。
验证顺序:先比对新密码和确认密码是否一致,一致则查看原密码是否正确,然后在更改密码
重点看第一第二问题
3.1 后台密码方式具体讲解
因为我是在 Redis 里面存的用户名,我们根据用户名来修改密码,先拿到Redis里的账号
//从redisutil里面拿出相应的角色account
CustomUserDetails user = (CustomUserDetails) redisUtil.get("userInfo");
//拿出username等于account,传如mapper
String accounts = user.getUsername();
进行输入的明文老密码passwordOld 和数据库中的加密的密码进行比较,调用了BCryptPasswordEncoder一个方法,用法如下
new BCryptPasswordEncoder().matches(明文密码例如“123”,数据库中的加密密码“密文。。。”)
方法中前一个参数为前端传来的值(例如123),后一个为数据库中需要对比的值(已加密存入数据库的密码)
boolean matches() 方法:尝试对整个目标字符展开匹配检测,也就是只有整个目标字符串完全匹配时才返回真值.
然后如果比较为真的话,将新密码加密后保存到数据库
userVo.setPassword(new BCryptPasswordEncoder().encode(userVo.getPasswordNew()));
这行代码就是将新密码加密 set 进 userVo 对象里
完整判断方法:
if(new BCryptPasswordEncoder().matches(userVo.getPasswordOld(),list.getPassword())){
userVo.setPassword(new BCryptPasswordEncoder().encode(userVo.getPasswordNew()));
userMapper.updatePassword(userVo);
return true;
}
完整密码效验方法(重写service接口的自定义方法)
图片讲解
代码(service实现类)
@Override
public boolean updatePassword(UserVo userVo) {
if(userVo.getPasswordNew().equals("")||userVo.getPasswordOld().equals("")||userVo.getPasswordSure().equals("")){
return false;
}
else if (!userVo.getPasswordNew().equals(userVo.getPasswordSure())){
return false;
}
//从redisutil里面拿出相应的角色account
CustomUserDetails user = (CustomUserDetails) redisUtil.get("userInfo");
//拿出username等于account,传如mapper
String accounts = user.getUsername();
userVo.setAccount(accounts);
UserVo list = userMapper.list(accounts);
if(new BCryptPasswordEncoder().matches(userVo.getPasswordOld(),list.getPassword())){
userVo.setPassword(new BCryptPasswordEncoder().encode(userVo.getPasswordNew()));
userMapper.updatePassword(userVo);
return true;
}
return false;
}
xml
<update id="updatePassword" parameterType="com.qcby.teach.help.entity.vo.UserVo">
update sys_user set password =#{userVo.password} where account=#{userVo.account}
</update>
Controller
@RequestMapping("updatePassword")
public ResultJson updatePassword(@RequestBody UserVo userVo){
log.info("入参{}",userVo);
if(userService.updatePassword(userVo)){
log.info("出参{}",ResultJson.ok("更改成功"));
return ResultJson.ok("更改成功");
}
else{
log.info("出参{}",ResultJson.error("输入有误,应重新输入"));
return ResultJson.error("输入有误,应重新输入");
}
}
3.2后台Spring全部代码
Controller
package com.step.controller.rest;
/**
* @author step
* @date 2021年07月19日 9:39
*/
@RequestMapping(GlobalConstant.REST_URL_PREFIX+"/user")
@RestController
@Slf4j
public class RestUserController {
@Autowired
private UserService userService;
/**
* 用户更改密码--》用户表
* @author step
* @date 2021/7/19 10:05
* @param userVo
* http://localhost:8080/rest/user/updatePassword
*/
@RequestMapping("updatePassword")
public ResultJson updatePassword(@RequestBody UserVo userVo){
log.info("入参{}",userVo);
if(userService.updatePassword(userVo)){
log.info("出参{}",ResultJson.ok("更改成功"));
return ResultJson.ok("更改成功");
}
else{
log.info("出参{}",ResultJson.error("输入有误,应重新输入"));
return ResultJson.error("输入有误,应重新输入");
}
}
}
实体类
Service
package com.step.service;
/**
* @author step
* @date 2021年07月19日 17:42
*/
public interface UserService {
/**
* 用户个人信息
* @author step
* @date 2021/7/30 9:59
* @return com.qcby.teach.help.entity.vo.UserVo
*/
UserVo listUserInfo();
/**
* 用户修改密码
* @author step
* @date 2021/7/31 20:29
* @param userVo
* @return boolean
*/
boolean updatePassword(UserVo userVo);
}
ServiceImpl (Service实现类)
package com.step.service.impl;
/**
* @author step
* @date 2021年07月19日 17:44
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RedisUtil redisUtil;
@Override
public UserVo listUserInfo() {
//从redisutil里面拿出相应的角色account
CustomUserDetails user = (CustomUserDetails) redisUtil.get("userInfo");
//拿出username等于account,传如mapper
String accounts = user.getUsername();
return userMapper.list(accounts);
}
@Override
public boolean updatePassword(UserVo userVo) {
if(userVo.getPasswordNew().equals("")||userVo.getPasswordOld().equals("")||userVo.getPasswordSure().equals("")){
return false;
}
else if (!userVo.getPasswordNew().equals(userVo.getPasswordSure())){
return false;
}
//从redisutil里面拿出相应的角色account
CustomUserDetails user = (CustomUserDetails) redisUtil.get("userInfo");
//拿出username等于account,传如mapper
String accounts = user.getUsername();
userVo.setAccount(accounts);
UserVo list = userMapper.list(accounts);
if(new BCryptPasswordEncoder().matches(userVo.getPasswordOld(),list.getPassword())){
userVo.setPassword(new BCryptPasswordEncoder().encode(userVo.getPasswordNew()));
userMapper.updatePassword(userVo);
return true;
}
return false;
}
}
Mapper
package com.step.mapper;
/**
* @author step
* @date 2021年07月19日 17:41
*/
public interface UserMapper extends BaseMapper<User> {
UserVo list(@Param("account")String account);
boolean updatePassword(@Param("userVo") UserVo userVo);
}
Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.teach.help.mapper.UserMapper">
<!-- 略有删除,只留下最重要的 -->
<!-- 个人信息每人仅一条 -->
<select id="list" resultType="com.qcby.teach.help.entity.vo.UserVo">
SELECT
id,
account,
password,
FROM
sys_user
<where>
<if test="account!=null">
account=#{account}
</if>
</where>
</select>
<update id="updatePassword" parameterType="com.qcby.teach.help.entity.vo.UserVo">
update sys_user set password =#{userVo.password} where account=#{userVo.account}
</update>
</mapper>
3.3 前台Vue全部代码
前台我用的Element自定义校验规则我喜欢管这个叫饿了么,没接广告!哈哈哈
Password.vue
<template>
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="原密码" prop="passwordOld">
<el-input v-model.number="ruleForm.passwordOld"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="passwordNew">
<el-input type="password" v-model="ruleForm.passwordNew" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="passwordSure">
<el-input type="password" v-model="ruleForm.passwordSure" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
var checkPasswordOld = (rule, value, callback) => {
if (!value) {
return callback(new Error('原密码不能为空'));
}
};
var validatePasswordNew = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入新密码'));
} else {
if (this.ruleForm.passwordSure !== '') {
this.$refs.ruleForm.validateField('passwordSure');
}
callback();
}
};
var validatePasswordSure = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.ruleForm.passwordNew) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
ruleForm: {
passwordOld: '',
passwordNew: '',
passwordSure: ''
},
rules: {
passwordNew: [
{ validator: validatePasswordNew, trigger: 'blur' }
],
passwordSure: [
{ validator: validatePasswordSure, trigger: 'blur' }
],
passwordOld: [
{ validator: checkPasswordOld, trigger: 'blur' }
]
}
};
},
methods: {
submitForm(formName) {
let params={
passwordNew:this.ruleForm.passwordNew,
passwordSure:this.ruleForm.passwordSure,
passwordOld:this.ruleForm.passwordOld
}
console.log(params)
this.$api.userUser.password(params).then((res) => {
// this.$message({ message: '修改成功,请重新登录', type: 'success' })
alert("修改成功,请重新登录!")
this.$api.login.logout().then((res)=>{
this.addVisible = false
sessionStorage.removeItem("user");
Cookies.remove('token',-1)
this.$router.push
("/login");
})
})
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style scoped>
</style>
修改成功便更改跳转到登录页面,达成闭环
this.$router.push
("/login");
userStudent.js
// 修改密码
export const password = (data) => {
return axios({
url: '/rest/user/updatePassword',//后台请求地址
method: 'post',//请求方式
data//data为变量,其内部结构为json数组,相当于ajax中的data:[{}]
})
}