在学习shiro中遇到很多问题,网上资料较少,只有硬啃英文,但demo太少,不好理解。ITeye博客中开涛对编写了比较全的系列教程
跟我学Shiro目录贴
,提供给大家学习,感谢开涛。
在学习到第十三章 RememberMe——《跟我学Shiro》的时候,有一个问题困扰了我2天,给开涛留言,至今未回复,功夫不复有心人,今天早上得到了解决。
问题描述:
1.按照开涛的测试过程
1、访问http://localhost:8080/chapter13/,会跳转到登录页面,登录成功后会设置会话及rememberMe Cookie;
2、关闭浏览器,此时会话cookie将失效;
3、然后重新打开浏览器访问http://localhost:8080/chapter13/,还是可以访问的;
4、如果此时访问http://localhost:8080/chapter13/authenticated.jsp,会跳转到登录页面重新进行身份验证。
测试本章节demo时发现记住我功能不能实现,仔细思考为什么呢?
2.查看cookie,仅生成了sid的cookie,没有生成rememberMe,究竟哪里出了问题?
3.debug跟踪源码
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info; try { info = authenticate(token); } catch (AuthenticationException ae) { try { onFailedLogin(token, ae, subject); } catch (Exception e) { if (log.isInfoEnabled()) { log.info("onFailedLogin method threw an " + "exception. Logging and propagating original AuthenticationException.", e); } } throw ae; //propagate } Subject loggedIn = createSubject(token, info, subject); onSuccessfulLogin(token, info, loggedIn); return loggedIn; }
从onSuccessfulLogin(token, info, loggedIn);这里一路逛追,直至
private javax.crypto.Cipher newCipherInstance(boolean streaming) throws CryptoException { String transformationString = getTransformationString(streaming); try { return javax.crypto.Cipher.getInstance(transformationString); } catch (Exception e) { String msg = "Unable to acquire a Java JCA Cipher instance using " + javax.crypto.Cipher.class.getName() + ".getInstance( \"" + transformationString + "\" ). " + getAlgorithmName() + " under this configuration is required for the " + getClass().getName() + " instance to function."; throw new CryptoException(msg, e); } }
在这里 return javax.crypto.Cipher.getInstance(transformationString);抛出异常:
org.apache.shiro.crypto.CryptoException: Unable to acquire a Java JCA Cipher instance using javax.crypto.Cipher.getInstance( "AES/CBC/PKCS5Padding" ). AES under this configuration is required for the org.apache.shiro.crypto. AesCipherService instance to function. ...... Caused by: java.security.NoSuchAlgorithmException: Cannot find any provider supporting AES/CBC/PKCS5Padding at javax.crypto.Cipher.getInstance(Cipher.java:523) at org.apache.shiro.crypto.JcaCipherService.newCipherInstance (JcaCipherService.java:408) ... 28 more
4.不明觉历!!!!!!!!!!!难道是开涛的代码有问题??还是故意设置了障碍要大家学习(实践证明这些假设都是错的)
5.网上查了很多资料,未果,分析源码CookieRememberMeManager继承了AbstractRememberMeManager,在AbstractRememberMeManager中的构造方法中默认初始化了加密算法与设置了默认的cipherKey
public AbstractRememberMeManager() { this.serializer = new DefaultSerializer<PrincipalCollection>(); this.cipherService = new AesCipherService(); setCipherKey(DEFAULT_CIPHER_KEY_BYTES); }
问题的结点可以定在cipherService的实现或者cipherKey的配置上,但很快排除了这一假设。
6.回顾第5章节的例子
@Test public void testAesCipherService() { CipherService cipherService = new AesCipherService(); // aesCipherService.setKeySize(128);//设置key长度 // byte[] key = aesCipherService.generateNewKey().getEncoded(); // String base64 = Base64.encodeToString(key); byte[] key = Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="); System.out.println("key2==" + key.toString()); System.out.println(Base64.encodeToString(key)); // 生成key // Key key = aesCipherService.generateNewKey(); String text = "hello"; System.out.println(new String(text.getBytes())); // 加密 try { String encrptText = cipherService.encrypt(text.getBytes( ), key).toHex(); System.out.println("encrptText==" + encrptText); // 解密 String text2 = new String(cipherService.decrypt( Hex.decode(encrptText), key).getBytes()); System.out.println("text2==" + text2); Assert.assertEquals(text, text2); } catch (CryptoException e) { e.printStackTrace(); } }
不断调试,右击项目属性--Properties----Java Build Path-----Libraries----JRE System Libraries,修改为jdk_1.7.0.01_x86(名称是添加jre时自定义的),测试代码,可以运行通过。原来是jdk的设置不正确!!!!我的电脑上安装了x86,x64的jdk,平时使用的是x64的版本。
7.以为大功告成,可是……使用jetty启动、使用tomcat启动,测试失败!啊啊啊!!要疯的节奏
8.难道是环境变量也要改过来?修改jdk环境变量%JAVA_HOME%为x86的版本之后,测试成功!终于松了一口气。
记录此问题的解决过程,虽然仅仅是jdk版本的问题,可能安装了x86 jdk的人永远不会遇到,但是还是费了我不少功夫去排除道道屏障,最终拨云见日。
结论:
1.shiro的加密使用javax.crypto.Cipher.getInstance获得实例,不支持64bit的jdk
2.遇到这个错误不用改环境变量,可以通过配置运行时的jre解决
a)tomcat:window->perferences->server->runtime Env--Edit Server--修改JRE为x86版本
b)jetty:Debug As--Run Configurations---JRE修改为x86版本
(此问题为电脑上同时安装了x64、x86版本jdk的伙伴记录)