今天項目啟動后登錄項目,突然爆出Unable to execute 'doFinal' with cipher instance錯誤。清除cookie登錄測試,又不報錯了,以前也見過類似問題,因為不影響使用,於是就忽略了,今天又遇到了,特研究一下。
原來,項目中使用Shiro作為認證權限控制框架,問題就出在RememberMe功能的配置上。問題產生的原因是rememberMe的cookie在第二次打開頁面后shiro無法解密。
項目配置
1 @Bean 2 public RememberMeManager rememberMeManager() { 3 CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); 4 //注入自定義cookie(主要是設置壽命, 默認的一年太長)
5 SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); 6 simpleCookie.setHttpOnly(true); 7 //設置RememberMe的cookie有效期為7天
8 simpleCookie.setMaxAge(604800); 9 rememberMeManager.setCookie(simpleCookie);10 return rememberMeManager; 11 }
框架源碼
1 public AbstractRememberMeManager() { 2 this.serializer = new DefaultSerializer<PrincipalCollection>(); 3 AesCipherService cipherService = new AesCipherService(); 4 this.cipherService = cipherService; 5 setCipherKey(cipherService.generateNewKey().getEncoded()); 6 } 7
8 public void setCipherKey(byte[] cipherKey) { 9 //Since this method should only be used in symmetric ciphers 10 //(where the enc and dec keys are the same), set it on both:
11 setEncryptionCipherKey(cipherKey); 12 setDecryptionCipherKey(cipherKey); 13 }
rememberMeManager繼承了AbstractRememberMeManager,然而AbstractRememberMeManager的構造方法中每次都會重新生成對稱加密密鑰,意味着每次重啟程序都會重新生成一對加解密密鑰。
這就會導致了,第一次啟動程序shiro使用A密鑰加密了cookie,第二次啟動程序shiro重新生成了密鑰B,當用戶訪問頁面時,shiro會用密鑰B去解密上一次用密鑰A加密的cookie,導致解密失敗,導致報錯,所以這不影響用戶登錄操作(rememberMe失效罷了),所以這種異常只會在程序重啟(shiro清除session)第一次打開頁面的時候出現。
解決辦法:手動設置對稱加密秘鑰。
1 //手動設置對稱加密秘鑰,防止重啟系統后系統生成新的隨機秘鑰,防止導致客戶端cookie無效
2 rememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j3Y+R1aSn5BOlAA=="));