14| 注册 - 加密鉴权&加密算法-MD5/SHA256 小滴课堂讲师 2024年08月05日 预计阅读 6 分钟 原文 ### 微服务注册接口逻辑 service - 邮箱验证码验证 - 密码加密(TODO) - 账号唯一性检查(TODO) - 插入数据库 - 新注册用户福利发放(TODO) ### 密码学-彩虹表暴力破解 例子:https://zhuanlan.zhihu.com/p/347964030 信息安全的基本目标: 1. **保密性**:防止用户的数据被不合法的用户读取 2. **完整性**:保护数据免受未授权的修改,包括数据的未授权创建和删除。 3. **可用性**:保证具有授权的一方可以访问所有资源 常见加密算法的分类:**哈希算法(单向加密),对称加密,非对称加密**。 **hash算法-单项加密**:目标文件(哈希Hash)通过哈希函数生成一个复杂字符串。 - 加密过程中`不需要使用密钥`,输入明文后由系统直接经过加密算法`处理成密文`,`密文无法解密`。 只有重新输入明文,并经过`同样的加密算法处理`,得到`相同的密文`并被系统重新识别后,才能真正解密 - 算法:`MD5/SHA1/SHA224/SHA256/` - 优点:`快速计算m`,具有单向性 one-way,不可由散列值推出原消息 - 场景:`文件完整性校验和(Checksum)算法`、常规密码等 #### 彩虹表暴力破解 - 网站:https://www.cmd5.com/ - 密码存储常用方式 - 双重MD5 - MD5+加盐 - 双重MD5+加盐 - 有些人会说为啥不直接用最强的Hash加密算法 ? - **更安全的算法,加密解密更复杂,接口性能下降更严重** | 算法 | 类型 | 结果长度 | 备注 | | ------ | ---- | -------- | -------| | MD5 | Hash | 128-bit | 已攻破 | | SHA1 | Hash | 160-bit | 已攻破 | | SHA224 | Hash | 224-bit | | | SHA256 | Hash | 256-bit | 推荐使用 | | SHA384 | Hash | 384-bit | | | SHA512 | Hash | 512-bit | | ### 加密逻辑完善 pom文件和common项目添加依赖检查 ```java org.apache.commons commons-lang3 commons-codec commons-codec ``` #### 加密逻辑 - 工具类编写CommonUtil:`获取随机长度的串`,用于加密的使用的秘钥。带有自定义的字符串 ```java private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /** * 获取随机长度的串 * @param length 长度 */ public static String getStringNumRandom(int length) { //生成随机数字和字母, Random random = new Random(); StringBuilder saltString = new StringBuilder(length); for (int i = 1; i <= length; ++i) { saltString.append(ALL_CHAR_NUM.charAt(random.nextInt(ALL_CHAR_NUM.length()))); } return saltString.toString(); } ``` #### 定义注册请求参数实体UserRegisterRequest ```java @ApiModel(value = "用户注册对象",description = "用户注册请求对象") @Data public class UserRegisterRequest { @ApiModelProperty(value = "昵称",example = "Anna小姐姐") private String name; @ApiModelProperty(value = "密码",example = "12345") private String pwd; @ApiModelProperty(value = "头像",example = "xx") @JsonProperty("head_img") private String headImg; @ApiModelProperty(value = "用户个人性签名",example = "人生需要动态规划,学习需要贪心算法") private String slogan; @ApiModelProperty(value = "0表示女,1表示男",example = "1") private Integer sex; @ApiModelProperty(value = "邮箱",example = "794666918@qq.com") private String mail; @ApiModelProperty(value = "验证码",example = "232343") private String code; } ``` 创建发送验证码字典`nla.common.enums.SendCodeEnum` ```java public enum SendCodeEnum { /** * 用户注册 */ USER_REGISTER; } ``` 创建缓存key的定义: `nla.common.constant.CacheKey` ```java public class CacheKey { /** * 注册验证码,第一个是类型,第二个是接收号码 */ public static final String CHECK_CODE_KEY = "code:%s:%s"; } ``` 将【[12| 注册安全攻防-邮箱发送验证码](https://blog.virtualagent.cn/blog/details/id/192)】中的邮件发送验证码的控制器逻辑,封装到一个NotifyService接口中,该接口提供`sendCode`接口。替换cacheKey。同时,请求参数变更为:`(SendCodeEnum sendCodeEnum, String to)` ```java // 原内容 String cacheKey = String.format(from + "_%s", to); // 替换内容 String cacheKey = String.format(CacheKey.CHECK_CODE_KEY,sendCodeEnum.name(),to); ``` NotifyService中添加**校验验证码方法checkCode** ```java public boolean checkCode(SendCodeEnum sendCodeEnum, String to, String code) { String cacheKey = String.format(CacheKey.CHECK_CODE_KEY, sendCodeEnum.name(), to); String cacheValue = redisTemplate.opsForValue().get(cacheKey); if (StringUtils.isNotBlank(cacheValue)) { String cacheCode = cacheValue.split("_")[0]; if (cacheCode.equals(code)) { //删除验证码 redisTemplate.delete(cacheKey); return true; } } return false; } ``` #### 编写注册接口controller ```java @ApiOperation("用户注册") @PostMapping("register") public JsonData register(@ApiParam("用户注册对象") @RequestBody UserRegisterRequest registerRequest){ return userService.register(registerRequest); } ``` #### 注册功能-验证码校验&加密密码 **加盐,生成密钥** ```java @Service @Slf4j public class UserServiceImpl extends ServiceImpl implements UserService { @Resource private NotifyService notifyService; @Resource private UserMapper userMapper; /** * 用户注册 * 邮箱验证码验证 * 密码加密(TODO) * 账号唯一性检查(TODO) * 插入数据库 * 新注册用户福利发放(TODO) */ @Override public JsonData register(UserRegisterRequest registerRequest) { boolean checkCode = false; //校验验证码 if (StringUtils.isNotBlank(registerRequest.getMail())) { checkCode = notifyService.checkCode(SendCodeEnum.USER_REGISTER, registerRequest.getMail(), registerRequest.getCode()); } if (!checkCode) { return JsonData.buildResult(BizCodeEnum.CODE_ERROR); } UserDO userDO = new UserDO(); BeanUtils.copyProperties(registerRequest, userDO); userDO.setCreateTime(new Date()); userDO.setSlogan("人生需要动态规划,学习需要贪心算法"); //设置密码 生成秘钥 盐 userDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8)); //密码+盐处理 String cryptPwd = Md5Crypt.md5Crypt(registerRequest.getPwd().getBytes(), userDO.getSecret()); userDO.setPwd(cryptPwd); //账号唯一性检查 794666918@qq.com if (checkUnique(userDO.getMail())) { int rows = userMapper.insert(userDO); log.info("rows:{},注册成功:{}", rows, userDO); //新用户注册成功,初始化信息,发放福利等 TODO userRegisterInitTask(userDO); return JsonData.buildSuccess(); } else { return JsonData.buildResult(BizCodeEnum.ACCOUNT_REPEAT); } } /** * 校验用户账号唯一 * * @param mail 邮箱地址 */ private boolean checkUnique(String mail) { return userMapper.selectList(new QueryWrapper().eq("mail", mail)).size() <= 0; } /** * 用户注册,初始化福利信息 TODO * * @param userDO 用户对象信息 */ private void userRegisterInitTask(UserDO userDO) { } } ```
评论区