加盐密码校验

加盐密码校验,第1张

过完年,因为业务,需要对采购单进行改价限制,通过密码去修改价格,做了一个加盐的方式,进行密码校验,现在刚好有时间进行整理,列举一下接口,和底层的代码逻辑。

1.先说一下整体逻辑:
设置密码:
(1)前端先调取后端,获取随机生成的8位key,拿到这个8位随机生成的key,后端将8位随机数存入redis。
(2)用户输入的6位数字与刚刚的8位key进行des加密然后传到后端,后端拿着加密后的密码,以及对应的用户唯一标识,去redis中拿去8位随机数,然后进行des解密,获取用户的6位密码,然后随机生成10位数(也就是盐),然后将6位数密码和10位数盐进行MD5加密,然后入库即可。

若要修改密码:
和上面类似,需要输入已有密码,再输入新密码,将前端输入的已有密码与已存在的盐进行MD5加密,再和数据库中的密码进行equals比较,若相同,则校验密码正确,再对新密码进行md5加密入库即可。

流程图:
● 加密:DES( 明文 + passwordKey[8位随机数])
● 解密:DES( 密文 + passwordKey[8位随机数])
● 数据库存储:MD5(明文 + salt[10位随机数])


2.密码的安全性如何保证:
<1>密码传给后端时如何防止被抓包,给后端传数据的时候就是哈希加盐的。盐存在哪里? 各个存储密码的业务方自己去生成盐、保存盐。
<2> 数据库存储密码,必须为密文存储。

3.sql新增字段

ALTER TABLE db_user
ADD COLUMN password VARCHAR(50) DEFAULT NULL COMMENT '开关密码,用户级别',
ADD COLUMN salt VARCHAR(20) DEFAULT NULL COMMENT '密码的盐';

4.下面是主要的代码逻辑:
(1)获取加密的key
(2)查询是否存在密码
(3)校验密码
(4)设置密码
(5)修改密码

Controller

/**
 * 密码
 */
@RestController
@RequestMapping("/api/rest/password")
@Api(tags = "密码")
@Slf4j
public class PasswordController {

    @DubboReference(version="0.1", timeout = 10000)
    private PasswordService passwordService;

    @ApiOperation(value = "获取加密的key")
    @GetMapping("/encryptKey")
    public WebResponse<String> encryptKey(User user) {
        long startTime = System.currentTimeMillis();
        String result = passwordService.getEncryptKey(user);
        log.info("获取加密的key,costTime: {},用户id: {},结果:{}", System.currentTimeMillis() - startTime, user.getTenantId(), result);
        return WebResponse.success(result);
    }

    @ApiOperation(value = "查询是否存在密码")
    @GetMapping("/checkExist")
    public WebResponse<Boolean> checkExist(User user) {
        long startTime = System.currentTimeMillis();
        Boolean result = passwordService.checkExist(user);
        log.info("查询是否存在密码,costTime: {},用户id: {},结果:{}", System.currentTimeMillis() - startTime, user.getTenantId(), result);
        return WebResponse.success(result);
    }

    @ApiOperation(value = "校验密码")
    @PostMapping("/checkPassword")
    public WebResponse<String> checkPassword(User user, @RequestBody @Validated CheckPasswordRequest checkPasswordRequest) {
        long startTime = System.currentTimeMillis();
        log.info("校验密码,用户id:{},参数:{}", user.getTenantId(), checkPasswordRequest.toString());
        CheckPasswordRequestTo checkPasswordRequestTo = JsonUtils.convertValue(checkPasswordRequest, CheckPasswordRequestTo.class);
        String result = passwordService.checkPassword(user, checkPasswordRequestTo);
        log.info("设置密码,costTime:{},用户id:{},结果:{}", System.currentTimeMillis() - startTime, user.getTenantId(), result);
        if (StringUtils.isEmpty(result)) {
            return WebResponse.success("校验密码成功");
        }
        return WebResponse.fail(result);
    }

    @ApiOperation(value = "设置密码")
    @PostMapping("/setPassword")
    public WebResponse<String> setPassword(User user, @RequestBody @Validated SetPasswordRequest setPasswordRequest) {
        long startTime = System.currentTimeMillis();
        log.info("设置密码,用户id:{},参数:{}", user.getTenantId(), setPasswordRequest.toString());
        SetPasswordRequestTo setPasswordRequestTo = JsonUtils.convertValue(setPasswordRequest, SetPasswordRequestTo.class);
        String result = passwordService.setPassword(user, setPasswordRequestTo);
        log.info("设置密码,costTime:{},用户id:{},结果:{}", System.currentTimeMillis() - startTime, user.getTenantId(), result);
        if (StringUtils.isEmpty(result)) {
            return WebResponse.success("设置成功,请保管好你的支付密码");
        }
        return WebResponse.fail(result);
    }

    @ApiOperation(value = "修改密码")
    @PostMapping("/modifyPassword")
    public WebResponse<String> modifyPassword(User user, @RequestBody @Validated PModifyPasswordRequest modifyPasswordRequest) {
        long startTime = System.currentTimeMillis();
        log.info("修改密码,用户id:{},参数:{}", user.getTenantId(), modifyPasswordRequest.toString());
        ModifyPasswordRequestTo modifyPasswordRequestTo = JsonUtils.convertValue(modifyPasswordRequest, ModifyPasswordRequestTo.class);
        String result = passwordService.modifyPassword(user, modifyPasswordRequestTo);
        log.info("修改密码,costTime:{},用户id:{},结果:{}", System.currentTimeMillis() - startTime, user.getTenantId(), result);
        if (StringUtils.isEmpty(result)) {
            return WebResponse.success("修改密码成功");
        }
        return WebResponse.fail(result);
    }
}

service

@DubboService(version="0.1")
@Slf4j
public class PasswordServiceImpl implements PasswordService {

    private final static int ENCRYPT_KEY_LENGTH = 8;
    private final static String CACHE_ENCRYPT_KEY_PREFIX = "password.";
    private final static Pattern PASSWORD_PATTERN = Pattern.compile("\d{6}");

    @Autowired
    private RedisService redisService;
    @Autowired
    private DBService dbService;

    /**
     * 使用des加密一个8位长的校验字符
     */
    @Override
    public String getEncryptKey(User user) {
        // 缓存的key 为用户级别
        String cacheKey = CACHE_ENCRYPT_KEY_PREFIX + user.getId();
        String encryptKey = redisService.get(cacheKey);
        String value;
        if (!StringUtils.isEmpty(encryptKey)) {
            value = encryptKey;
        }
        else {
            value = DesUtil.randomString(ENCRYPT_KEY_LENGTH);
        }
        redisService.set(cacheKey, value, 1, TimeUnit.DAYS);
        log.info("获取加密的随机数,用户id:{},value:{}", user.getId(), value);
        return value;
    }

    @Override
    public Boolean checkExist(User user) {
        DBData existData = dbService.query(user));
        log.info("是否存在密码,用户id:{},结果:{}", user.getId(), existData.getPassword());
        return !StringUtils.isEmpty(existData.getPassword());
    }

    /**
     * 校验密码
     */
    @Override
    public String checkPassword(User user, PasswordRequestTo param) {
        log.info("校验密码,用户id:{},输入的密码:{}", user.getId(), param.toString());
        // 1.获取缓存中的8位加密随机数
        String encryptKey = redisService.get(CACHE_ENCRYPT_KEY_PREFIX + user.getId());
        if (StringUtils.isEmpty(encryptKey)) {
            log.info("校验密码,没有拿到缓存的des加密随机数,用户id:{}", user.getId());
            return "调用超时,请刷新重试";
        }
        DBData existData = dbService.query(user);
        // 2.若没有密码或者加盐字段,则表示没有输入过密码
        if (StringUtils.isEmpty(existData.getPassword()) || StringUtils.isEmpty(existData.getSalt())) {
            log.info("用户没有原始密码,数据异常,租户id:{}", user.getId());
            return "用户没有原始密码";
        }
        String originalEncodePassword = existData.getPassword();
        String originalSalt = existData.getSalt();
        // 3.解密前端传来的密码,和已存在的盐进行hash加密后,进行比较
        String inputPassword = DesUtil.decode(param.getEncodedPassword(), encryptKey);
        String checkPassword = MD5Utils.hash(inputPassword + originalSalt);
        if (!Objects.equals(checkPassword, originalEncodePassword)) {
            log.info("校验密码错误,用户id:{},输入的密码:{}", user.getId(), inputPassword);
            return "输入密码错误";
        }
        log.info("校验成功,用户id:{}", user.getId());
        return null;
    }

    /**
     * 此方法仅为第一次设置密码
     */
    @Override
    public String setPassword(User user, SetPasswordRequestTo param) {
        log.info("设置密码,用户id:{}", user.getId());
        // 1.校验两次输入密码一致
        if (!Objects.equals(param.getEncodedPassword(), param.getSecondEncodedPassword())) {
            log.info("设置密码,两次密码不一致,用户id:{},密码:{},确认密码:{}",
                    user.getTenantId(), param.getEncodedPassword(), param.getSecondEncodedPassword());
            return "两次输入密码不一致";
        }
        // 2.获取缓存中的8位加密随机数
        String encryptKey = redisService.get(CACHE_ENCRYPT_KEY_PREFIX + user.getId());
        if (StringUtils.isEmpty(encryptKey)) {
            log.info("设置密码,没有拿到缓存的des加密随机数,用户id:{}", user.getId());
            return "调用超时,请刷新重试";
        }
        // 3.判断是否已存在密码
        DBData existData = dbService.query(user);
        if (!StringUtils.isEmpty(existData.getPassword()) || !StringUtils.isEmpty(existData.getSalt())) {
            log.info("设置密码,用户已存在原始密码,用户id:{}", user.getId());
            return "租户已存在原始密码";
        }
        // 4.校验密码格式 只能是6位数字
        String inputPassword = DesUtil.decode(param.getEncodedPassword(), encryptKey);
        boolean isPasswordNum = PASSWORD_PATTERN.matcher(inputPassword).matches();
        if (!isPasswordNum) {
            log.info("设置密码,输入的密码不是6位数字,用户id:{},解密后的密码为:{}", user.getId(), inputPassword);
            return "密码必须为6位数字";
        }
        // 5.设置密码
        String salt = RandomStringUtils.randomAlphanumeric(10);
        existData.setPassword(MD5Utils.hash(inputPassword + salt));
        existData.setSalt(salt);
        try {
            if (Objects.isNull(existData.getId()) || Objects.equals(0L, existData.getId())) {
                dbService.insertOne(user, existData);
                log.info("设置密码,新增配置数据,用户id:{}", user.getId());
            }
            else {
                dbService.updateOne(user, existData);
                log.info("设置密码,更新配置数据,用户id:{}", user.getId());
            }
            return null;
        }
        catch (Exception e) {
            log.error("设置密码异常,用户id:{}", user.getId(), e);
            return "设置密码异常";
        }
    }

    /**
     * 此方法为重新设置密码,先校验,再更新
     */
    @Override
    public String modifyPassword(User user, ModifyPasswordRequestTo param) {
        log.info("修改密码,用户id:{}", user.getId());
        // 1.校验修改的两次输入密码一致
        if (!Objects.equals(param.getEncodedPassword(), param.getSecondEncodedPassword())) {
            log.info("修改密码,两次密码不一致,用户id:{},密码:{},确认密码:{}",
                    user.getId(), param.getEncodedPassword(), param.getSecondEncodedPassword());
            return "两次输入密码不一致";
        }
        // 2.获取缓存中的8位加密随机数
        String encryptKey = redisService.get(CACHE_ENCRYPT_KEY_PREFIX + user.getId());
        if (StringUtils.isEmpty(encryptKey)) {
            log.info("修改密码,没有拿到缓存的des加密随机数,用户id:{}", user.getId());
            return "调用超时,请刷新重试";
        }
        DBData existData = dbService.query(user);
        // 3.若没有密码或者加盐字段,则表示没有输入过密码
        if (StringUtils.isEmpty(existData.getPassword()) || StringUtils.isEmpty(existData.getSalt())) {
            log.info("修改密码,租户没有原始密码,数据异常,用户id:{}", user.getId());
            return "租户没有原始密码";
        }
        // 4.先校验原始密码是否正确
        String originalEncodePassword = existData.getPassword();
        String originalSalt = existData.getSalt();
        String inputOriginalPassword = DesUtil.decode(param.getOldEncodedPassword(), encryptKey);
        String checkOriginalPassword = MD5Utils.hash(inputOriginalPassword + originalSalt);
        if (!Objects.equals(checkOriginalPassword, originalEncodePassword)) {
            log.info("修改密码,校验密码错误,用户id:{},输入的密码:{}", user.getId(), inputOriginalPassword);
            return "输入密码错误";
        }
        // 5.开始校验修改的密码 只能是6位数字
        String inputModifyPassword = DesUtil.decode(param.getEncodedPassword(), encryptKey);
        boolean isPasswordNum = PASSWORD_PATTERN.matcher(inputModifyPassword).matches();
        if (!isPasswordNum) {
            log.info("修改密码,输入的密码不是6位数字,用户id:{},解密后的密码为:{}", user.getId(), inputModifyPassword);
            return "密码必须为6位数字";
        }
        // 6.开始修改密码
        existData.setPayableOrderPassword(MD5Utils.hash(inputModifyPassword + originalSalt));
        try {
            dbService.updateOne(user, existData);
            log.info("修改密码,更新数据,用户id:{}", user.getId());
            return null;
        }
        catch (BusinessException e) {
            log.error("修改密码异常,用户id:{}", user.getId(), e);
            return "修改密码异常";
        }
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/788181.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存