转载

shiro中的rememberMe cookie

shiro的介绍在这里就不说了,主要讲下shiro的rememberMe cookie。在shiro中有一个类实现了rememberMe功能, org.apache.shiro.web.mgt.CookieRememberMeManager 。在登录时,代码也很简单:

UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    	token.setRememberMe(true);
    	subject.login(token);

这之后的大致流程如下:

  • 第一步直接将验证工作委托给securityManager。
  • 工作中一步步进行委托,securityManager -> authenticator -> realm…
  • 验证通过后将AuthenticationInfo结果返回到securityManager,securityManager将结果传递给RememberMeManager( org.apache.shiro.web.mgt.CookieRememberMeManager ),委托rememberMe的工作。

org.apache.shiro.web.mgt.CookieRememberMeManager 继承 AbstractRememberMeManager ,当登录成功后调用以下方法:

publicvoidonSuccessfulLogin(Subject subject, AuthenticationToken token, AuthenticationInfo info){
        this.forgetIdentity(subject);
        if(this.isRememberMe(token)) {
            this.rememberIdentity(subject, token, info);
        } else if(log.isDebugEnabled()) {
            log.debug("AuthenticationToken did not indicate RememberMe is requested. RememberMe functionality will not be executed for corresponding account.");
        }

    }

以上代码主要做了2件事:

  • 将已有的rememberMe cookie删除
  • 如果需要remember,则设置rememberMe cookie

设置rememberMe cookie的代码如下:

protectedvoidrememberSerializedIdentity(Subject subject,byte[] serialized){
        if(!WebUtils.isHttp(subject)) {
            if(log.isDebugEnabled()) {
                String request1 = "Subject argument is not an HTTP-aware instance. This is required to obtain a servlet request and response in order to set the rememberMe cookie. Returning immediately and ignoring rememberMe operation.";
                log.debug(request1);
            }

        } else {
            HttpServletRequest request = WebUtils.getHttpRequest(subject);
            HttpServletResponse response = WebUtils.getHttpResponse(subject);
            String base64 = Base64.encodeToString(serialized);
            Cookie template = this.getCookie();
            SimpleCookie cookie = new SimpleCookie(template);
            cookie.setValue(base64);
            cookie.saveTo(request, response);
        }
    }

其中 cookie.saveTo(request, response); 便是将base64编码后的值写到浏览器。

那么这个rememberMe cookie,也就是上面函数中的第二个参数 serialized 是如何生成的?

在上面的 onSuccessfulLogin 中, this.rememberIdentity(subject, token, info); 主要调用链如下:

publicvoidrememberIdentity(Subject subject, AuthenticationToken token, AuthenticationInfo authcInfo){
        PrincipalCollection principals = this.getIdentityToRemember(subject, authcInfo);
        this.rememberIdentity(subject, principals);
    }
protectedvoidrememberIdentity(Subject subject, PrincipalCollection accountPrincipals){
        byte[] bytes = this.convertPrincipalsToBytes(accountPrincipals);
        this.rememberSerializedIdentity(subject, bytes);
    }
protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
		//序列化principals对象,得到的值再经过aes加密和base64编码
        byte[] bytes = this.serialize(principals);
        if(this.getCipherService() != null) {
            bytes = this.encrypt(bytes);
        }

        return bytes;
    }
protected byte[] encrypt(byte[] serialized) {
        byte[] value = serialized;
        CipherService cipherService = this.getCipherService();
        if(cipherService != null) {
            ByteSource byteSource = cipherService.encrypt(serialized, this.getEncryptionCipherKey());
            value = byteSource.getBytes();
        }

        return value;
    }

所以得到rememberMe cookie过程如下:

  • 得到principals对象
  • 通过配置的key,使用aes加密
  • 将加密后的值再通过base64解密

当客户端带着这个rememberMe cookie访问时,将会按照下面的过程来寻找已记住的身份信息:

  • 获取rememberMe cookie的值
  • Base64解码
  • 使用AES解密
  • 使用ObjectInputStream进行反序列化

之前一直疑惑shiro是如何储存这个rememberMe cookie的,难道是持久化了?否则服务器重启后为什么这个cookie依旧有效。现在大致上就明白了,只有反序列化成功了,才能证明此rememberMe cookie的合法性。

因本人水平有限,若文章内容存在问题,恳请指出。允许转载,转载请在正文明显处注明原站地址以及原文地址,谢谢!

原文  http://vinoit.me/2017/02/03/shiro-rememberMe/
正文到此结束
Loading...