Spring Security 無法登陸,報錯:There is no PasswordEncoder mapped for the id “null”
編寫好繼承了WebSecurityConfigurerAdapter類的WebSecurityConfig類後,我們需要在configure(AuthenticationManagerBuilder auth) 方法中定義認證用於資訊獲取來源以及密碼校驗規則等。(configure函式名字不重要,官方用的好像是configureGlobal(……),重要的是在這個被@EnableWebSecurity或@EnableGlobalMethodSecurity,或者@EnableGlobalAuthentication註解 的類中配置了AuthenticationManagerBuilder)。
我一開始用的認證資訊獲取來源是記憶體獲取——inMemoryAuthentication,程式碼如下
-
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-
//inMemoryAuthentication 從記憶體中獲取
-
auth.inMemoryAuthentication().withUser("user1").password("123456").roles("USER");
-
}
使用的是spring security自帶的login頁面,結果登陸的時候,使用者名稱和密碼正確也無法開啟資源,還是停留在login頁面。而且發現控制檯報了異常——There is no PasswordEncoder mapped for the id “null”。
網上百度了一下發現這是因為Spring security 5.0中新增了多種加密方式,也改變了密碼的格式。
我們來看一下官方文件,以下是官方文件原話:
-------------------------------------------------------------------------------------------------------------------
The general format for a password is:
{id}encodedPassword
Such that id
is an identifier used to look up which PasswordEncoder
encodedPassword
is the original encoded password for the selected PasswordEncoder
. The id
must be at the beginning of the password, start with {
and end with }
. If the id
cannot be found, the id
will be null. For example, the following might be a list of passwords encoded using different id
. All of the original passwords are "password".
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG {noop}password {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc= {sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
-------------------------------------------------------------------------------------------------------------------
上面這段話的意思是說,現如今Spring Security中密碼的儲存格式是“{id}…………”。前面的id是加密方式,id可以是bcrypt、sha256等,後面跟著的是加密後的密碼。也就是說,程式拿到傳過來的密碼的時候,會首先查詢被“{”和“}”包括起來的id,來確定後面的密碼是被怎麼樣加密的,如果找不到就認為id是null。這也就是為什麼我們的程式會報錯:There is no PasswordEncoder mapped for the id “null”。官方文件舉的例子中是各種加密方式針對同一密碼加密後的儲存形式,原始密碼都是“password”。
要想我們的專案還能夠正常登陸,需要修改一下configure中的程式碼。我們要將前端傳過來的密碼進行某種方式加密,spring security 官方推薦的是使用bcrypt加密方式。那麼如何對密碼加密呢,只需要在configure方法裡面指定一下。
修改後是這樣的:
-
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-
//inMemoryAuthentication 從記憶體中獲取
-
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("user1").password(new BCryptPasswordEncoder().encode("123456")).roles("USER");
-
}
在inMemoryAuthentication()後面多了".passwordEncoder(new BCryptPasswordEncoder())",這相當於登陸時用BCrypt加密方式對使用者密碼進行處理。以前的".password("123456")" 變成了 ".password(new BCryptPasswordEncoder().encode("123456"))" ,這相當於對記憶體中的密碼進行Bcrypt編碼加密。比對時一致,說明密碼正確,允許登陸。
如果你現在用的也是從記憶體中取密碼,那麼按照上面這麼修改後應該會成功登入沒有問題的。
如果你用的是在資料庫中儲存使用者名稱和密碼,那麼一般是要在使用者註冊時就使用BCrypt編碼將使用者密碼加密處理後儲存在資料庫中。並且修改configure()方法,加入".passwordEncoder(new BCryptPasswordEncoder())",保證使用者登入時使用bcrypt對密碼進行處理再與資料庫中的密碼比對。如下:
-
//注入userDetailsService的實現類
-
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());