Springboot项目-修改密码

  • 1. 找回密码
  • 2. 验证短信code
  • 3. 修改密码
  • 4. 代码工具类


如下代码,是用户在忘记密码的情况下,通过手机号重置密码,没有用户登陆的token,关于在设置token值,在我的另一边文章有讲到,欢迎小伙伴一起来参与讨论

1. 找回密码

需求说明:

1. 获取短信验证码: 通过手机号来判断用户是否存在,生成短信验证码并且保存到redis中,设置超时时间
2. 下一步: 检查短信验证码是否正确
3. 提交:  修改密码

springboot动态更新mysql密码 springboot修改密码代码_redis


接收参数的form类

@Data
public class FindPasswordVo {

    @Pattern(regexp = RegexUtil.PHONE, message = "请填写正确的手格式")
    private String phone;

}

controller控制层

@Resource
private UserService userService;

@ApiOperation("找回密码")
@PostMapping("/findPassword")
public ResultVO<Object> findPassword(
			@Valid @RequestBody FindPasswordVo findPasswordVo,
            					BindingResult bindingResult) {
    Map<String, String> resultMap = userService.findPassword(findPasswordVo);
    return ResultVO.success(resultMap);
}

service业务层

//接口抽象方法
	Map<String, String> findPassword(FindPasswordVo findPasswordVo);

	@Override
	public Map<String, String> findPassword(FindPasswordVo findPasswordVo) {

       // 验证手机号是否存在数据库中
       Integer count = userMapper.selectCount(
               new LambdaQueryWrapper<User>().eq(User::getPhone, findPasswordVo.getPhone()));

       if (count != 1) {
           throw new ResultException(ResultEnum.ACCOUNT_NOT_EXIST);
           //账户不存在,可以抛出模糊的异常,手机号错误,账号密码在错误
       }

       HashMap<String, String> hashMap = new HashMap<>(2);

       // todo 调用短信接口
       String smsCode = RandomUtil.createCode();

       try {
           SmsUtil.sendSimpleSms(findPasswordVo.getPhone(), SmsType.SMS_CAR);
       } catch (ClientException e) {
           log.error("send sms error,", e);
       }

       // 吧短信code 放在redis里面 失效时间为5分钟
       redisTemplate.opsForValue().set(FormTipsConstant.FIND_PASSWORD + findPasswordVo.getPhone(), 
       									smsCode, 5, TimeUnit.MINUTES);

       // key 到时候乣前段传过来
       hashMap.put("key", findPasswordVo.getPhone());
       // todo 上线关闭
       hashMap.put("code", smsCode);

       return hashMap;
   }

2. 验证短信code

接收参数的form类

@Data
public class CheckSmsCodeVo {

    @Pattern(regexp = RegexUtil.PHONE, message = "请填写正确的手格式")
    private  String phone;

    @NotEmpty(message = FormTipsConstant.SMS_CODE_IS_NOT_EMPTY)
    private String code;

}

controller控制层

@ApiOperation("验证短信code")
@PostMapping("/checkSmsCode")
public ResultVO<Object> checkSmsCode(
		@Valid @RequestBody CheckSmsCodeVo checkSmsCodeVo,
                        	BindingResult bindingResult) {
    userService.checkSmsCode(checkSmsCodeVo);
    return ResultVO.success();
}

service层

void checkSmsCode(CheckSmsCodeVo checkSmsCodeVo);//接口
	
	@Override
	public void checkSmsCode(CheckSmsCodeVo checkSmsCodeVo) {
	    String code = redisTemplate.opsForValue().get(
	    			FormTipsConstant.FIND_PASSWORD + checkSmsCodeVo.getPhone());
	    if (StringUtil.isEmpty(code)) {
	        throw new ResultException(ResultEnum.SMS_CODE_LOSE);
	    }
	    // 验证码错误
	    if (!code.equals(checkSmsCodeVo.getCode())) {
	        throw new ResultException(ResultEnum.SMS_CODE_IS_NOT_TURE);
	    }
	}

3. 修改密码

接收参数的form类

@ApiModel("修改密码接收类")
@Data
public class ChangePasswordVo {


    @Pattern(regexp = RegexUtil.PHONE, message = FormTipsConstant.SMS_CODE_IS_NOT_EMPTY)
    @ApiModelProperty("手机号码")
    private String phone;

    @NotEmpty(message = "密码不能为空")
    @ApiModelProperty("密码")
    private String password;

    @NotEmpty(message = "确认密码不能为空")
    @ApiModelProperty("确认密码")
    private String rePassword;

}

controller控制层

@ApiOperation("修改密码")
@PostMapping("/changePassword")
public ResultVO<Object> changePassword(
		@Valid @RequestBody ChangePasswordVo changePasswordVo,
                            BindingResult bindingResult) {
    userService.changePassword(changePasswordVo);
    return ResultVO.success();
}

service层

void changePassword(ChangePasswordVo changePasswordVo);
	
	@Override
	public void changePassword(ChangePasswordVo changePasswordVo) {
	    String code = redisTemplate.opsForValue().get(
	    			FormTipsConstant.FIND_PASSWORD + changePasswordVo.getPhone());
	    if (StringUtil.isEmpty(code)) {
	        throw new ResultException(ResultEnum.SMS_CODE_LOSE);
	    }
	    if (!changePasswordVo.getPassword().equals(changePasswordVo.getRePassword())) {
	        throw new ResultException(ResultEnum.PASSWORD_NOT_SAME);
	    }
	    User user = userMapper.selectOne(
	            new LambdaQueryWrapper<User>().eq(User::getPhone, changePasswordVo.getPhone()));
	    if (null == user) {
	        throw new ResultException(ResultIf.DATA_NOT_EXISTS);
	    }
	    String encrypt = PasswordUtil.encrypt(changePasswordVo.getPassword(), user.getEncrypt());
	    user.setPassword(encrypt);
	    userMapper.updateById(user);
	}

4. 代码工具类

1.1 返回结果的枚举类

@Getter
public enum ResultEnum implements ResultIf {

    /**
     * ===========================系统基本返回===============================
     */
    SYSTEM_ERROR(-3, "系统繁忙,请稍后重试"),

    ACCOUNT_EXPIRE(-2, "账号过期"),

    UN_LOGIN(-1, "未登录"),
    SUCCESS(0, "成功"),
    ILLEGAL_REQUEST(1, "非法请求"),
    VALIDATOR_ERROR(2, "验证失败"),
    SQL_ERROR(3, "数据库操作失败"),
    DATA_NOT_EXISTS(4, "数据不存在"),
    DATA_DUPLICATE(5, "不允许添加重复数据"),
    PARAMETER_EMPTY_ERROR(6, "必填参数不能为空"),
    DATA_DIRTY_READ_ERROR(7, "数据异常"),
    NO_HAS_JURISDICTION(8, "无权限进行此操作"),


    IMAGE_CODE_ERROR(10001, "图形验证码不正确或已失效"),
    DELETE_DATA_NOT_EXISTS(40001, "删除数据存在审核通过的数据"),

    IMAGE_ERROR(6000, "上传图片失败"),
    IMAGE_DOWNLOAD_ERROR(6001, "下载图片失败"),
    IMAGE_NOT_EXISTS(6002, "图片不存在"),
    IS_NOT_IMAGE(6003, "上传的不是图片或者为空"),
    IMAGE_SIZE_ERROR(6004, "图片的大小超过5M"),

    LOCATION_NUM_ALREADY_MAX(6005, "推荐位置数量已到6个"),

    WE_CHAT_DOWNLOAD_ERROR(190003, "请点击右上角,在浏览器中打开重试"),
    NO_SELECT_TARGET(190004, "请选择对应链接"),

    VERSION_TOO_SMALL(200001, "版本号必须大于之前的版本号"),

    /**
     * ===========================登录账号信息相关===============================
     */
    PHONE_INVALID(7000, "注册电话不合法"),
    PHONE_IS_INVALID(7001, "手机号格式不对"),
    PASSWORD_NOT_SAME(7002, "密码与确认密码不相同"),
    OLD_PASSWORD_INCORRECT(7003, "原密码不正确"),
    PASSWORD_SHOULD_NOT_SAME_AS_OLD(7004, "密码不能与原密码相同"),
    ACCOUNT_IS_DISABLE(7005, "您的账号已不可用,请联系管理员"),
    ACCOUNT_PASSWORD_NOT_EXIST(7006, "账号或密码不正确"),
    ACCOUNT_NOT_EXIST(7012, "账号不存在"),
    ACCOUNT_EXIST_REGISTER(7007, "账号已被注册"),
    ACCESS_TOKEN_EMPTY_ERROR(7008, "access_token为空"),
    OPEN_ID_EMPTY_ERROR(7009, "open_id为空"),
    WECHAT_PARAMS_ERROR(7009, "微信配置参数异常"),
    INVITE_CODE_ERROR(7011, "邀请码不正确"),
    PASSWORD_FORMAT_ERROR(7012, "密码格式错误"),
    ACCOUNT_IS_HAS(7013, "该账号已被注册"),
    ACCOUNT_NO_HAS(7014, "该账号不存在"),
    ACCOUNT_HAS_WECHAT(7014, "该微信已经绑定过账号"),
    INVITE_CODE_SAME_AS_PHONE(7015, "不能填写自己的号码作为邀请码"),
    PHONE_BIND_ANOTHER_WECHAT(7016, "手机号已绑定其他微信"),
    ACCOUNT_HAS_PARENT_ALREADY(7017, "您已经绑定过上级"),
    INVITATION_CODE_IS_NOT_ALLOWED_YOUR_SUBORDINATES(7018, "邀请码不允许填写自己的下级"),
    ACCOUNT_ALREADY_EXIST_IN_HISTORY(7019, "此管理员账号存在历史数据,不允许添加"),

    /**
     * ===========================验证码===============================
     */
    CODE_IS_NOT_TURE(170, "图形验证码错误"),
    CODE_LOSE(171, "图形验证码已失效"),
    SMS_CODE_LOSE(172, "短信验证码已失效"),
    SMS_CODE_IS_NOT_TURE(173, "图形验证码错误"),

    /**
     * ===========================文档上传===============================
     */
    DOC_UPLOAD_ERROR(2000, "上传的不是文档或者为空")

    private int code;
    private String msg;

    ResultEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

1.2 random工具类

public class RandomUtil {
    public static final int RANDOM_TYPE_UUID = 0;
    public static final int RANDOM_TYPE_NUM = 1;
    public static final int RANDOM_TYPE_LOW = 2;
    public static final int RANDOM_TYPE_NUM_LOW = 3;
    public static final int RANDOM_TYPE_UP = 4;
    public static final int RANDOM_TYPE_NUM_UP = 5;
    public static final int RANDOM_TYPE_LOW_UP = 6;
    public static final int RANDOM_TYPE_NUM_LOW_UP = 7;
    private static final String ALL_NUM = "0123456789";
    private static final String LOW_CASE = "abcdefghijklmnopqrstuvwxyz";
    private static final String UP_CASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final List<String> STRINGS = Arrays.asList("0123456789", "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    private static final int RANDOM_NUM_LENGTH = 11;
    private static Random r = new Random();

    private RandomUtil() {
    }
	
	//获取uuid
    public static String uuid() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    public static String getRandom(int length, int type) {
        if (type == 0) {
            return uuid().substring(0, length);
        } else {
            StringBuilder buffer = new StringBuilder(26);
            StringBuilder sb = new StringBuilder(length);

            int range;
            for(range = 0; range < STRINGS.size(); ++range) {
                if ((type & 1 << range) == 1 << range) {
                    buffer.append((String)STRINGS.get(range));
                }
            }

            range = buffer.length();

            for(int i = 0; i < length; ++i) {
                sb.append(buffer.charAt(r.nextInt(range)));
            }

            return sb.toString();
        }
    }

    public static String getRandom() {
        long l = r.nextLong();
        String str = Long.toUnsignedString(l);
        return fillZero(str);
    }

    private static String fillZero(String str) {
        return str.length() >= 11 ? str.substring(0, 11) : fillZero("0" + str);
    }
	
	//创建随机的6位数
    public static String createCode() {
        return getRandom(6, 1);
    }

    public static String getRandomFileName() {
        String y = TimeUtil.nowTimeInt();
        return y + r.nextInt();
    }
}

1.3 不变量 数据

public class FormTipsConstant {
    public static final String SMS_CODE_IS_NOT_EMPTY = "请输入正确的手机号码";

    /**
     * ********************************redis前缀*******************************
     */

    /**
     * 找回密码前缀
     */
    public static final String FIND_PASSWORD = "fp:";

    /**
     * 每天登录加pro值
     */
    public static final String DAY_PRO = "dayPro:";



    private FormTipsConstant() {
    }
}

1.4 短信接口

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created on 17/6/7.
 * 短信API产品的DEMO程序,工程中包含了一个SmsDemo类,直接通过
 * 执行main函数即可体验短信产品API功能(只需要将AK替换成开通了云通信-短信产品功能的AK即可)
 * 工程依赖了2个jar包(存放在工程的libs目录下)
 * 1:aliyun-java-sdk-core.jar
 * 2:aliyun-java-sdk-dysmsapi.jar
 * <p>
 * 备注:Demo工程编码采用UTF-8
 * 国际短信发送请勿参照此DEMO
 */
public class SmsUtil {

    /**
     * 产品名称:云通信短信API产品,开发者无需替换
     */
    private static final String PRODUCT = "Dysmsapi";
    /**
     * 产品域名,开发者无需替换
     */
    private static final String DOMAIN = "dysmsapi.aliyuncs.com";

    private static final String ACCESS_KEY_ID = "LTAIUVUSXkZjvL2A";
    private static final String ACCESS_KEY_SECRET = "fmnlEFHjoQu8iVNoCuucDbjHQNLoXD";

    public static SendSmsResponse sendSms(String phone, String code, String smsType) throws ClientException {

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");

        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SendSmsRequest request = new SendSmsRequest();
        //必填:待发送手机号
        request.setPhoneNumbers(phone);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName("clara官方商城");
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(smsType);
        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        request.setTemplateParam("{\"code\":\"" + code + "\"}");

        //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
        //request.setSmsUpExtendCode("90997");

        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
        request.setOutId("yourOutId");

        //hint 此处可能会抛出异常,注意catch
        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);

        return sendSmsResponse;
    }
    public static SendSmsResponse sendSimpleSms(String phone, String smsType) throws ClientException {

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SendSmsRequest request = new SendSmsRequest();
        //必填:待发送手机号
        request.setPhoneNumbers(phone);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName("clara官方商城");
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(smsType);

        //hint 此处可能会抛出异常,注意catch
        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);

        return sendSmsResponse;
    }


    public static QuerySendDetailsResponse querySendDetails(String bizId) throws ClientException {

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象
        QuerySendDetailsRequest request = new QuerySendDetailsRequest();
        //必填-号码
        request.setPhoneNumber("17398378986");
        //可选-流水号
        request.setBizId(bizId);
        //必填-发送日期 支持30天内记录查询,格式yyyyMMdd
        SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
        request.setSendDate(ft.format(new Date()));
        //必填-页大小
        request.setPageSize(10L);
        //必填-当前页码从1开始计数
        request.setCurrentPage(1L);

        //hint 此处可能会抛出异常,注意catch
        QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request);

        return querySendDetailsResponse;
    }

}

密码工具类

1. 当我们修改密码的时候,需要给新密码加 salt 值,在存入数据库
2. 初始情况下,设置一个默认的 salt 值,存到数据库
import org.springframework.util.DigestUtils;

/**
 * @author Tiger on 18-6-28 下午5:01
 */
public class PasswordUtil {

    // x
    public static String encrypt(String password, String salt) {

        byte[] saltMd5 = DigestUtils.md5Digest(salt.getBytes());
        byte[] passwordByte = DigestUtils.md5Digest(password.getBytes());
        byte[] resultByte = new byte[32];
        for (int i = 0; i < passwordByte.length && i < saltMd5.length; i++) {
            resultByte[2 * i] = saltMd5[i];
            resultByte[2 * i + 1] = passwordByte[i];
        }

        return DigestUtils.md5DigestAsHex(resultByte);
    }
}