解決Spring security5.5.7報(bào)錯(cuò)Encoded password does not look like BCrypt異常
背景
一個(gè)老項(xiàng)目,由于2022年爆發(fā)了spring bean和spring core的漏洞,將springboot從1.5.4升級(jí)到2.5.14版本,修復(fù)了spring的漏洞。
同時(shí)spring security也需要同步升級(jí),升級(jí)過(guò)程中出現(xiàn)了一系列錯(cuò)誤。
故做一個(gè)記錄在此。
問(wèn)題:
登錄權(quán)限系統(tǒng)時(shí),出現(xiàn)Encoded password does not look like BCrypt異常錯(cuò)誤;同時(shí)報(bào)出clientSecret不匹配的問(wèn)題。
解決方案
統(tǒng)一采用PasswordEncoderFactories.createDelegatingPasswordEncoder()去獲取到密碼加密器PasswordEncoder
(1)新版本的Spring Security對(duì)客戶端的密鑰進(jìn)行了加密處理,配置中需要使用PasswordEncoderFactories.createDelegatingPasswordEncoder().encode進(jìn)行加密;
(2)登錄成功后的handler,則需要采用PasswordEncoderFactories.createDelegatingPasswordEncoder().matches(clientSecret,clientDetails.getClientSecret())判斷密鑰是否匹配,而不是采用原有舊版的未加密的密鑰進(jìn)行equal進(jìn)行比較字符串。
BCryptPasswordEncoder介紹
Spring Security 中提供了 BCryptPasswordEncoder用于用戶密碼的加密和驗(yàn)證,這里講解一下該 PasswordEncoder 的實(shí)現(xiàn)邏輯.
首先 BCryptPasswordEncoder 使用了 BCrypt 算法來(lái)對(duì)密碼實(shí)現(xiàn)加密和驗(yàn)證。由于 BCrypt本身是一種 單向Hash算法,因此它和我們?nèi)粘S玫?MD5一樣,通常情況下是無(wú)法逆向解密的。
在 BSD系統(tǒng)中 BCrypt 算法主要用來(lái)替代 md5 加密算法,它使用了一種可變版本的Blowfish流密碼算法。通過(guò)多次加鹽和隨機(jī)數(shù),因此這套加密算法被廣泛用于許多系統(tǒng)的密碼加密當(dāng)中。
然而每次加密的結(jié)果是不一樣的,如果采用兩次加密的結(jié)果進(jìn)行equal比較,那是得不到真實(shí)的true結(jié)果的。
具體代碼改動(dòng)
- 注冊(cè)一個(gè)bean,覆蓋原有的PasswordEncoder
/** * 加密方式,spring security5.5.7升級(jí)后,默認(rèn)采用BCryptPasswordEncoder * @return */ @Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); }
- 項(xiàng)目啟動(dòng)時(shí),加載的認(rèn)證服務(wù)器配置的修改
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 引入BCrypt強(qiáng)哈希加密和解密工具 */ @Autowired private PasswordEncoder passwordEncoder; /* * 客戶端配置改動(dòng) * * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); //spirng boot 1.5.* 升級(jí)到spring boot 2.0以上,當(dāng)再次訪問(wèn)授權(quán)服務(wù)器時(shí)出現(xiàn)Encoded password does not look like BCrypt異常,需要passwordEncoder.encode if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) { for (OAuth2ClientProperties config : securityProperties.getOauth2().getClients()) { //設(shè)置clientid builder.withClient(config.getClientId()) // 設(shè)置clientsecret,需要加密配置 .secret(passwordEncoder.encode(config.getClientSecret())) // 設(shè)置令牌過(guò)期時(shí)間,單位秒,默認(rèn)7200,定義在OAuth2ClientProperties .accessTokenValiditySeconds(config.getAccessTokenValidateSeconds()) // 允許的授權(quán)模式 .authorizedGrantTypes("refresh_token", "authorization_code", "password") // 設(shè)置刷新令牌的過(guò)期時(shí)間,單位秒,這里設(shè)置為60天 .refreshTokenValiditySeconds(5184000) // 配置oauth能獲取的權(quán)限,是一個(gè)數(shù)組 .scopes("all", "write", "read"); } } }
- 構(gòu)造用戶登錄信息
@Component public class MyUserDetailsService implements UserDetailsService { /** * 注入加密器 */ @Autowired private PasswordEncoder passwordEncoder; /** * 搜索用戶信息,構(gòu)造登錄用戶 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //其他數(shù)據(jù)庫(kù)查詢等邏輯省略........ logger.info("表單登錄用戶名:" + username); //進(jìn)行權(quán)限系統(tǒng)登錄,密碼前面需要加上加密的方式,調(diào)用createDelegatingPasswordEncoder后默認(rèn)會(huì)加上{bcrypt}在密碼前面 String password = passwordEncoder.encode("123456"); user.setLastLoginTime(nowTime); sysPublicUserRepository.save(user); logger.info("保存登錄時(shí)間:" + nowTime); User user1 = new User(userId, password, true, accountNonExpired, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles())); return user1; } }
- 登錄成功后SuccessHandler的校驗(yàn)
@Component("authenticationSuccessHandler") public class authenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { /** * 注入密碼器 */ @Autowired private PasswordEncoder passwordEncoder; /** * 此方法是用于在request中取得ClientDetails和新建tokenRequest,并用這兩個(gè)參數(shù)來(lái)生成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對(duì)應(yīng)的配置信息不存在:" + clientId); } else if (!passwordEncoder.matches(clientSecret,clientDetails.getClientSecret())) { //關(guān)鍵是這里不能用equal匹配了 throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + clientId); } //............ //后續(xù)token的其他處理 //............ } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 使用spring?security?BCryptPasswordEncoder接入系統(tǒng)
- 如何在spring boot項(xiàng)目中使用Spring Security的BCryptPasswordEncoder類進(jìn)行相同密碼不同密文的加密和驗(yàn)證
- 一文掌握SpringSecurity?BCrypt密碼加密和解密
- Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn)
- SpringBoot整合BCrypt實(shí)現(xiàn)密碼加密
- Spring security BCryptPasswordEncoder密碼驗(yàn)證原理詳解
- Spring項(xiàng)目使用Maven和BCrypt實(shí)現(xiàn)修改密碼功能方式
相關(guān)文章
Java實(shí)戰(zhàn)小技巧之?dāng)?shù)組與list互轉(zhuǎn)
在Java中,經(jīng)常遇到需要List與數(shù)組互相轉(zhuǎn)換的場(chǎng)景,下面這篇文章主要給大家介紹了關(guān)于Java實(shí)戰(zhàn)小技巧之?dāng)?shù)組與list互轉(zhuǎn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-08-08Java微信公眾平臺(tái)開(kāi)發(fā)(5) 文本及圖文消息回復(fù)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開(kāi)發(fā)第五步,回文本及圖文消息回復(fù)的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04IntelliJ?IDEA2022中的Java文檔注釋設(shè)置、操作方法
這篇文章主要介紹了IntelliJ?IDEA2022中的Java文檔注釋設(shè)置、操作詳述,本文通過(guò)圖文并茂的方式給大家介紹IDEA2022?文檔注釋設(shè)置方法,需要的朋友可以參考下2022-08-08詳解java.lang.reflect.Modifier.isInterface()方法
這篇文章主要介紹了詳解java.lang.reflect.Modifier.isInterface()方法的相關(guān)資料,這里提供實(shí)例幫助大家理解這個(gè)方法的使用,需要的朋友可以參考下2017-09-09Java實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07springboot 1.5.2 集成kafka的簡(jiǎn)單例子
本篇文章主要介紹了springboot 1.5.2 集成kafka的簡(jiǎn)單例子 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11