解决Spring security5.5.7报错Encoded password does not look like BCrypt异常

 更新时间:2024年08月14日 09:35:40   作者:kevingavinhu  
这篇文章主要介绍了解决Spring security5.5.7出现Encoded password does not look like BCrypt异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

背景        

一个老项目,由于2022年爆发了spring bean和spring core的漏洞,将springboot从1.5.4升级到2.5.14版本,修复了spring的漏洞。

同时spring security也需要同步升级,升级过程中出现了一系列错误。

故做一个记录在此。

问题:

登录权限系统时,出现Encoded password does not look like BCrypt异常错误;同时报出clientSecret不匹配的问题。

解决方案

统一采用PasswordEncoderFactories.createDelegatingPasswordEncoder()去获取到密码加密器PasswordEncoder

(1)新版本的Spring Security对客户端的密钥进行了加密处理,配置中需要使用PasswordEncoderFactories.createDelegatingPasswordEncoder().encode进行加密;

(2)登录成功后的handler,则需要采用PasswordEncoderFactories.createDelegatingPasswordEncoder().matches(clientSecret,clientDetails.getClientSecret())判断密钥是否匹配,而不是采用原有旧版的未加密的密钥进行equal进行比较字符串。

BCryptPasswordEncoder介绍

Spring Security 中提供了 BCryptPasswordEncoder用于用户密码的加密和验证,这里讲解一下该 PasswordEncoder 的实现逻辑.

首先 BCryptPasswordEncoder 使用了 BCrypt 算法来对密码实现加密和验证。由于 BCrypt本身是一种 单向Hash算法,因此它和我们日常用的 MD5一样,通常情况下是无法逆向解密的。

在 BSD系统中 BCrypt 算法主要用来替代 md5 加密算法,它使用了一种可变版本的Blowfish流密码算法。通过多次加盐和随机数,因此这套加密算法被广泛用于许多系统的密码加密当中。

然而每次加密的结果是不一样的,如果采用两次加密的结果进行equal比较,那是得不到真实的true结果的。

具体代码改动

  • 注册一个bean,覆盖原有的PasswordEncoder
	/**
	 * 加密方式,spring security5.5.7升级后,默认采用BCryptPasswordEncoder
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}
  • 项目启动时,加载的认证服务器配置的修改
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    /**
     * 引入BCrypt强哈希加密和解密工具
     */
    @Autowired
    private PasswordEncoder passwordEncoder;
	
	/*
     * 客户端配置改动
     *
     * @param clients
     * @throws Exception
     */
    
     @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
 
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        //spirng boot 1.5.* 升级到spring boot 2.0以上,当再次访问授权服务器时出现Encoded password does not look like BCrypt异常,需要passwordEncoder.encode
        if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {
            for (OAuth2ClientProperties config : securityProperties.getOauth2().getClients()) {
                //设置clientid
                builder.withClient(config.getClientId())
                        // 设置clientsecret,需要加密配置
                        .secret(passwordEncoder.encode(config.getClientSecret()))
                        // 设置令牌过期时间,单位秒,默认7200,定义在OAuth2ClientProperties
                        .accessTokenValiditySeconds(config.getAccessTokenValidateSeconds())
                        // 允许的授权模式
                        .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                        // 设置刷新令牌的过期时间,单位秒,这里设置为60天
                        .refreshTokenValiditySeconds(5184000)
                        // 配置oauth能获取的权限,是一个数组
                        .scopes("all", "write", "read");
            }
        }
}
  • 构造用户登录信息
@Component
public class MyUserDetailsService implements UserDetailsService {
    /**
	 * 注入加密器
	 */
    @Autowired
	private PasswordEncoder passwordEncoder;
	/**
	 * 搜索用户信息,构造登录用户
	 * @param username
	 * @return
	 * @throws UsernameNotFoundException
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		//其他数据库查询等逻辑省略........
		logger.info("表单登录用户名:" + username);
		
//进行权限系统登录,密码前面需要加上加密的方式,调用createDelegatingPasswordEncoder后默认会加上{bcrypt}在密码前面
		String password = passwordEncoder.encode("123456");
		user.setLastLoginTime(nowTime);
		sysPublicUserRepository.save(user);
		logger.info("保存登录时间:" + nowTime);
		User user1 = new User(userId, password,
				true, accountNonExpired, true, true,
				AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));
 
		return user1;
	}
}
  • 登录成功后SuccessHandler的校验
@Component("authenticationSuccessHandler")
public class authenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
 
	/**
	 * 注入密码器
	 */
	@Autowired
	private PasswordEncoder passwordEncoder;
	/**
	 * 此方法是用于在request中取得ClientDetails和新建tokenRequest,并用这两个参数来生成OAuth2AccessToken
	 */
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
		//..............省略代码
		//..............
		
		//客户端的密钥,从header中取出
		String clientSecret = extractAndDecodeHeader(header, request);
		
		ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
		PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
		if (clientDetails == null) {
			throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在:" + clientId);
		} else if (!passwordEncoder.matches(clientSecret,clientDetails.getClientSecret())) { //关键是这里不能用equal匹配了
			throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + clientId);
		}
		//............
		//后续token的其他处理
		//............
	}
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java实战小技巧之数组与list互转

    Java实战小技巧之数组与list互转

    在Java中,经常遇到需要List与数组互相转换的场景,下面这篇文章主要给大家介绍了关于Java实战小技巧之数组与list互转的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-08-08
  • Java微信公众平台开发(5) 文本及图文消息回复的实现

    Java微信公众平台开发(5) 文本及图文消息回复的实现

    这篇文章主要为大家详细介绍了Java微信公众平台开发第五步,回文本及图文消息回复的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • 详解@Autowired是如何注入变量的

    详解@Autowired是如何注入变量的

    在 Spring 容器中,当我们想给某一个属性注入值的时候,有多种不同的方式,例如使用 @Autowired、@Inject等注解,下面小编就来和小伙伴们聊一聊,@Autowired 到底是如何把数据注入进来的
    2023-07-07
  • Java基于zxing生成二维码矩阵过程解析

    Java基于zxing生成二维码矩阵过程解析

    这篇文章主要介绍了Java基于zxing生成二维码矩阵过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • 如何为Logback日志添加唯一追踪ID

    如何为Logback日志添加唯一追踪ID

    本文介绍了如何为Logback日志添加唯一追踪ID,以便在测试和调试时更容易定位报错信息,通过创建过滤器和修改配置文件,可以在每个请求的日志中添加唯一的ID,并将其返回给前端,这样,当用户反馈报错时,开发人员可以根据ID快速定位和解决问题
    2024-12-12
  • IntelliJ IDEA2022中的Java文档注释设置、操作方法

    IntelliJ IDEA2022中的Java文档注释设置、操作方法

    这篇文章主要介绍了IntelliJ IDEA2022中的Java文档注释设置、操作详述,本文通过图文并茂的方式给大家介绍IDEA2022 文档注释设置方法,需要的朋友可以参考下
    2022-08-08
  • 详解java.lang.reflect.Modifier.isInterface()方法

    详解java.lang.reflect.Modifier.isInterface()方法

    这篇文章主要介绍了详解java.lang.reflect.Modifier.isInterface()方法的相关资料,这里提供实例帮助大家理解这个方法的使用,需要的朋友可以参考下
    2017-09-09
  • 深入解析Java中的内部类

    深入解析Java中的内部类

    这篇文章主要介绍了Java中的内部类,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-07-07
  • Java实现简易学生管理系统

    Java实现简易学生管理系统

    这篇文章主要为大家详细介绍了Java实现简易学生管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • springboot 1.5.2 集成kafka的简单例子

    springboot 1.5.2 集成kafka的简单例子

    本篇文章主要介绍了springboot 1.5.2 集成kafka的简单例子 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11

最新评论