记-JWT 认证接口性能调优

记-JWT 认证接口性能调优

一、前言

1.服务器

4核16G

2.工具

工具用途
jmeter压力测试工具
arthas阿里开源的Java诊断工具

2.描述

使用jmeter压测jwt认证接口时发现,服务器CPU占用特别高,TPS总是上不去仅有50 tps

二、问题排查

使用arthas 诊断工具连续打印CPU使用率高的线程堆栈,发现该方法占用CPU资源高org.springframework.security.crypto.bcrypt.BCrypt#checkpw(java.lang.String, java.lang.String)


之前对BCryptPasswordEncoder接触不多,于是顺便了解了下 Spring security BCryptPasswordEncoder密码验证原理。

BCryptPasswordEncoder密文实际由 BCryptVersion(版本号) + strength(长度) + ‘SecureRandom(随机值即real_salt)’ + hashed(散列结果);也就是说BCryptPasswordEncoder加密结果实际是加盐的hash结果,而盐不同,加密结果也就不相同,这也是为什么多次执行org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder#encode加密操作返回结果不一样,这和MD5不一样的地方。比对密码时,将数据库的hash结果作为salt(伪盐),利用org.springframework.security.crypto.bcrypt.BCrypt#checkpw(java.lang.String, java.lang.String)提取真正的real_salt(z真盐),将传入的原始密码加密,real_salt一样的情况下,重新hash的结果也是一样的,从而达到比对校验的目的。

示例数据:$2a 10 10 10SMx6IiquDTyLMrdr3n8o1.MBn/8.Y2u5hH.qVSGMafX3wsJR8VJ9q

1.加密

org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder#encode

	public String encode(CharSequence rawPassword) {
		if (rawPassword == null) {
			throw new IllegalArgumentException("rawPassword cannot be null");
		}

		String salt;
		if (random != null) {
			salt = BCrypt.gensalt(version.getVersion(), strength, random);
		} else {
			salt = BCrypt.gensalt(version.getVersion(), strength);
		}
		return BCrypt.hashpw(rawPassword.toString(), salt);
	}

2.匹配

org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder#matches

	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		if (rawPassword == null) {
			throw new IllegalArgumentException("rawPassword cannot be null");
		}

		if (encodedPassword == null || encodedPassword.length() == 0) {
			logger.warn("Empty encoded password");
			return false;
		}

		if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
			logger.warn("Encoded password does not look like BCrypt");
			return false;
		}

		return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
	}

3.示例程序

        String password = "123456";
        // passwordEncoder实例
        final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        // 加密
        final String hashed = passwordEncoder.encode(password);
        // 匹配结果
        final boolean matches = passwordEncoder.matches(password, hashed);

匹配时,实际执行hash操作BCrypt.checkpw(rawPassword.toString(), encodedPassword);

4.总结

查询资料发现,前面讲的strength(长度)对hash性能影响极大(指数级别),可用值431,默认值10。测试发现确实如此。`strength(长度)`为4时,单次操作24毫秒;strength(长度)为20时,单次操作几分钟甚至更长时间。

三、解决办法

指定strength(长度)

    @Bean
    public PasswordEncoder passwordEncoder() {
        //一种基于随机生成salt的根据强大的哈希加密算法
        return new BCryptPasswordEncoder(4);
    }

四、效果

同一机器下,TPS从50提升值2000左右,提升40倍。

arthas诊断神器-初探
jmeter入门示例
Spring security BCryptPasswordEncoder密码验证原理详解

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬山境KL攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值