栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring security5.5.7出现Encoded password does not look like BCrypt异常

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring security5.5.7出现Encoded password does not look like BCrypt异常

目录

背景:

解决方案:

BCryptPasswordEncoder介绍

具体代码改动:


背景:

        一个老项目,由于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结果的。

具体代码改动:

(1)项目启动时,加载的认证服务器配置的修改

@Configuration
@EnableAuthorizationServer
public class CchwebAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Autowired
    private BCryptPasswordEncoder bcryptPasswordEncoder;
	
	
    
     @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异常,需要bCryptPasswordEncoder.encode
         PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        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");
            }
        }
	}
}

(2)构造用户登录信息

@Component
public class MyUserDetailsService implements UserDetailsService {
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		
		logger.info("表单登录用户名,这里的用户名是手机号:" + username);
		
		//进行权限系统登录,密码前面需要加上加密的方式,调用createDelegatingPasswordEncoder后默认会加上{bcrypt}在密码前面
		PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
		String password = passwordEncoder.encode("123456");
		logger.info("保存登录时间:" + nowTime);
		User user1 = new User(username, password,
				true, true, true, true,
				AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));

		return user1;
	}
}

(3)登录成功后SuccessHandler的校验

@Component("authenticationSuccessHandler")
public class authenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

	
	@Autowired
	private BCryptPasswordEncoder bcryptPasswordEncoder;
	
	@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的其他处理
		//............
	}
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/995658.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号