Springboot项目报文加密(采用AES、RSA动态加密策略)

1 : 痛点

在项目安全扫描中,发现仅采用秘钥加密的形式存在安全隐患。
如果秘钥泄露,那每一次的会话及请求的报文,则无异于暴露在外。
这种现象虽不太会发生,但秘钥一旦泄露,或被恶意攻破,会导致客户信息泄露,后果是很严重的。

2 : 思路

AES是对称加密,而RSA是非对称加密。
如果单采用AES对报文加密,虽然可行,但是存在风险,于是就想到了采用组合加密的形式。
具体思路:前后端互相交换RSA公钥、私钥。然后再进行AES秘钥交换。
目的:为每一次的会话,生成只有会话的前后端知道的AES秘钥,会话与会话之前秘钥不相同。
大致如下图:
在这里插入图片描述

3 : 前期准备

3.1 : AES加密

参考链接:https://blog.csdn.net/qq_38254635/article/details/129622075

3.2 : RSA加密

参考链接:https://blog.csdn.net/qq_38254635/article/details/129623413

3.3 : 登录验证码

参考链接:https://blog.csdn.net/qq_38254635/article/details/129735679

3.4 : BCrypt非对称加密

参考链接:https://blog.csdn.net/qq_38254635/article/details/129746320

4 : 代码设计

4.1 : 获取验证码

关键代码:

public Result getVerifyCode() throws IOException {
    String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
    log.info("The current verification code is:" + verifyCode + "\n");
    String verifyKey = SessionUtil.getUUId();
    SessionUtil.getSession().setAttribute(VerifyCodeUtils.VERIFY_CODE,verifyCode + "_" + System.currentTimeMillis());
    redisUtil.delete(RedisBean.VERIFY_CODE + verifyKey);
    redisUtil.setEx(RedisBean.VERIFY_CODE + verifyKey, verifyCode, 300);
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    VerifyCodeUtils.outputImage(VerifyCodeUtils.VERIFY_CODE_WIDE, VerifyCodeUtils.VERIFY_CODE_HIGH, output, verifyKey);
    String imageBase = Base64.getEncoder().encodeToString(output.toByteArray());
    Result<Map<String,Object>> result = new Result<>();
    result.setData(new HashMap<String,Object>(){{
        put("verifyKey", verifyKey);
        put("verifyIO", imageBase);
    }});
    return result;
}

4.2 : 获取RSA秘钥

关键代码:

public Result getRSAKey(){
    try {
        //first:generate RSA key pair
        String sessionKey = SessionUtil.getUUId();
        Map<String, Object> keyMap = RSAUtils.generateRSAKeyPair();
        String publicKey = RSAUtils.getRSAPublicKey(keyMap);
        String privateKey = RSAUtils.getRSAPrivateKey(keyMap);
        log.info("sessionKey:" + sessionKey + "\n" + "publicKey:" + publicKey + "\n" + "privateKey:" + privateKey);
        //second:save public key and private key to redis
        redisUtil.setEx(RedisBean.CLIENT_PUBLIC + sessionKey, publicKey,30, TimeUnit.MINUTES);
        redisUtil.setEx(RedisBean.CLIENT_PRIVATE + sessionKey, privateKey,30, TimeUnit.MINUTES);
        //third:return session key and public key to web
        Result<Map<String,Object>> result = new Result<>();
        result.setData(new HashMap<String,Object>(){{
            put("sessionKey", sessionKey);
            put("publicKey", publicKey);
        }});
        return result;
    } catch (Exception e){
        throw new RuntimeException("get RSA public key error", e);
    }
}

4.3 : 获取AES秘钥

关键代码:

public Result getAESKey(String param) throws Exception{
    Encrypt vo = JSON.parseObject(param, Encrypt.class);
    //first:decrypt param got web RSA public key.
    String sessionKey = vo.getSessionKey();
    String clientPrivateKey = redisUtil.get(RedisBean.CLIENT_PRIVATE + sessionKey);
    String webPublicKey = new String(RSAUtils.decryptByPrivateKey(Base64Utils.decode(vo.getEncryptedString()), clientPrivateKey));
    //second:generate AES key
    String aesKey = AesUtil.generateAESKey();
    redisUtil.setEx(RedisBean.AES_KEY + sessionKey, aesKey,30, TimeUnit.MINUTES);
    //third:use web RSA public key to encrypt AES key, return encrypted string
    String encryptedString = Base64Utils.encode(RSAUtils.encryptByPublicKey(aesKey.getBytes(), webPublicKey));
    return Result.success(encryptedString);
}

4.4 : 登录

关键代码:

public Result login(Encrypt encrypt){
    //1、使用sessionKey,从redis获取AES秘钥
    String aesKey = redisUtil.get(RedisBean.AES_KEY + encrypt.getSessionKey());
    //2、解密加密报文
    String json = AesUtil.decryptAes(encrypt.getEncryptedString(), aesKey);
    LoginVO vo = JSON.parseObject(json, LoginVO.class);
    if(null == vo) return Result.error("数据有误,请刷新当前页面");
    //3、校验验证码
    Result codeResult = this.checkVerifyCode(vo);
    if(codeResult.getCode() != 0) return codeResult;
    //4、校验账号密码
    SysUser user = sysUserService.queryOne(vo.getUserCode());
    if(StringUtils.isEmpty(user)) return Result.error("账号或密码有误,请核实!");
    if(!"1".equals(user.getStatus())) return Result.error("当前用户已失效,请核实!");
    //5、账号锁定功能
    Result lockResult = this.checkAccountLock(vo.getUserCode(), user.getPassword(), vo.getPassWord());
    if(lockResult.getCode() != 0) return codeResult;
    //6、初始化login信息
    LoginSession login = new LoginSession(user.getId(), user.getUserCode(), user.getUserName(),
            user.getOrgCode(), user.getStatus(), new Date(), aesKey);
    //7、生成token
    String token = SessionUtil.putLoginToSession(login, null);
    //8、清理redis中RSA、AES秘钥
    redisUtil.delete(RedisBean.CLIENT_PUBLIC + encrypt.getSessionKey());
    redisUtil.delete(RedisBean.CLIENT_PRIVATE + encrypt.getSessionKey());
    redisUtil.delete(RedisBean.AES_KEY + encrypt.getSessionKey());
    //9、返回前端参数
    Result<Map<String,Object>> result = new Result<>();
    result.setData(new HashMap<String,Object>(){{
        put("token", token);
        put("login", login);
    }});
    return result;
}

private Result checkVerifyCode(LoginVO vo){
    String code = redisUtil.get(vo.getVerifyCodeId());
    if(StringUtils.isEmpty(code)) code = (String) SessionUtil.getSession().getAttribute(VerifyCodeUtils.VERIFY_CODE);
    redisUtil.delete(vo.getVerifyCodeId());
    SessionUtil.getSession().removeAttribute(VerifyCodeUtils.VERIFY_CODE);
    if(!Optional.ofNullable(code).isPresent()) return Result.error(500, "验证码已失效");
    if(code.contains("_")){
        String[] splitCode = code.split("_");
        boolean isTimeOut = isTimeOut(Long.valueOf(splitCode[1]), 300L);
        if(isTimeOut) return Result.error(500, "验证码已失效");
        if(!vo.getVerifyCode().equalsIgnoreCase(splitCode[0])) return Result.error(500, "验证码输入有误");
    } else {
        if(!vo.getVerifyCode().equalsIgnoreCase(code)) return Result.error(500, "验证码输入有误");
    }
    return Result.ok();
}

private static boolean isTimeOut(Long var, Long intervalTime) {
    long nm = 1000;
    Date d1 = new Date(System.currentTimeMillis());
    Date d2 = new Date(var);
    long diff = d1.getTime() - d2.getTime();
    long minute = diff / nm;
    return minute > intervalTime;
}

private Result checkAccountLock(String userCode, String userPassword, String password){
    if(!"on".equals(LOCK_ACCOUNT_SWITCH)) {
        if(!PassWordUtil.check(password, userPassword))  return Result.error("账号或密码有误,请核实!");
        return Result.ok();
    }
    //校验已冻结帐号
    if (!StringUtils.isEmpty(redisUtil.get(RedisBean.FREEZE_ACCOUNT + userCode))) {
        Long time = redisUtil.getExpire(RedisBean.FREEZE_ACCOUNT + userCode);
        Long m = time/60;
        if(m == 0){
            return Result.error(500, "账号已锁定,请在" + time + "秒后重新登陆!");
        } else {
            return Result.error(500, "账号已锁定,请在" + m + "分钟后重新登陆!");
        }
    }
    if(!PassWordUtil.check(password, userPassword)){
        Long count = redisUtil.incrBy(RedisBean.FREEZE_COUNT + userCode, 1);
        //密码错误五次冻结
        if (count == 1) {
            redisUtil.expire(RedisBean.FREEZE_COUNT + userCode,1,TimeUnit.HOURS);
        }
        if (count >= WRONG_TIMES) {
            //冻结一小时
            redisUtil.setEx(RedisBean.FREEZE_ACCOUNT + userCode, "1", 3600);
            //重置次数
            redisUtil.delete(RedisBean.FREEZE_COUNT + userCode);
        }
        return Result.error("账号或密码有误,请核实!");
    }
    //重置错误验证次数
    if("on".equals(LOCK_ACCOUNT_SWITCH)) redisUtil.delete(RedisBean.FREEZE_COUNT + userCode);
    return Result.ok();
}

4.5 : 登出

关键代码:

public Result loginOut(String token) throws Exception {
    SessionUtil.removeLoginFromSession(token);
    return Result.ok();
}

5 : 资源下载

Springboot项目报文加密(AES、RSA、Filter动态加密):https://blog.csdn.net/qq_38254635/article/details/135273863
CSDN下载:https://download.csdn.net/download/qq_38254635/87620796
百度网盘下载:https://pan.baidu.com/s/1v7Wvev9w0xS-8-VAMEGOmw?pwd=wu8n
提取码:wu8n

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中使用RSA加密的步骤如下: 1. 首先,生成私钥和公钥。可以使用Java内置的KeyPairGenerator类来生成RSA密钥对,分别得到私钥和公钥。 2. 将生成的密钥放到application.properties文件中,可以使用spring-boot-starter-security等相关框架来管理密钥。 3. 前端发送请求时,向后端请求公钥。后端会随机获取公钥并将其返回给前端。 4. 前端使用开源的jsencrypt.js库来对需要加密的数据(例如密码)进行加密加密完成后,将加密后的数据传输到后端。 5. 后端接收到加密的数据后,使用私钥进行解密操作。 所以,使用RSA加密Spring Boot中的步骤主要包括生成密钥、传输公钥至前端、前端加密数据传输至后端后端使用私钥解密。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Springboot项目报文加密采用AESRSA动态加密策略)](https://download.csdn.net/download/qq_38254635/87620796)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Spring-boot如何使用RSA加密](https://blog.csdn.net/qq_39150049/article/details/109191120)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [SpringBoot通过RSA实现用户名和密码的加密和解密](https://blog.csdn.net/qq_36735969/article/details/124842616)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值