上篇 中我们对 securityContextHolderAwareRequestFilter的丰富多彩有了个体验, 最后对这个类的名字也做了一个望文生义的解释. 本篇中我们将接着看上篇提到的子类,即SavedRequestAwareWrapper. 这个类在父亲的基业上又有什么新的突破呢? 这得从它的贤内助说起, 即这个子类的属性savedRequest . 呵呵, 这也正是组合的好处.
我们看到SavedRequestAwareWrapper类里所有方法的实现都是基于这个贤内助的帮助. 拿我们熟悉的getCookies方法来说, 在执行这个方法时, 先要看贤内助的脸色: 如果 savedRequest没什么话说,才能执行父亲所传授的,即父类的方法 super.getCookies();
刚才看天下足球的"疯狂的足球", 而上面的内容也仅是我的想像,希望它较为贴切.
下面,我们看这个贤内助是怎么回事? Ta是何许人也呢? 读源码时发现, 这个 SavedRequest没什么背景, 没有继承, 只是实现了 Serializable接口. 那有什么用? 单独地看这个类是不行了, 不过发现这么一条语句: SavedRequest saved = (SavedRequest) session.getAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY); 这里有一个get,那也应该有"人"set过, 难道说通往发现之路的入口?
经过一段时间的侦查, 终于发现那别有洞天! 在描述这个洞天前, 先看这么一个实际例子.
在CSDN网站里下载东西, 刚开始时我们先找, 找到一个一看评价不错, 那就下载吧, 可点下载按钮时,CSDN网站把我们引到登录页面, 噢, 原来还没登录呢. 很麻利地填写了登录信息后,点确定, 这时CSDN直接就把刚才要下载的东西拿出来了. 8错! 登录完后, 不必再从头找那个链接了. 可这是"8错"是怎么实现的呢?
我们用Acegi里的SavedRequest来描述下这个功能的实现, 当然CSDN不一定是用Acegi来实现的, 这里只是借用下那个情景. 在点"下载"按钮前, CSDN是允许我们浏览的, 也就是我们有匿名浏览的权限, 可当下载时, CSDN发现这个人没有下载的权限, 得让Ta登录. 于是, CSDN里的安全机制让页面跳转到登录界面, 同时,为了用户的使用方便, 把用户刚才想下载的那个链接也记了下来, 放到了Session中, 而我们说的 SavedRequest正是干这个用的. 用户登录后, CSDN的安全机制再从Session中取出刚才那个想下载的链接, 于是下载顺利进行了.
下面结合前面介绍的Acegi概念,把这个过程再描述一遍. 刚才开始用户找东西时用浏览的权限, 当Ta想下载时, Acegi的 FilterSecurityInterceptor在执行 beforeInvocation方法时发现当前用户没有相应的权限, 于是 FilterSecurityInterceptor抛出一个 accessDeniedException异常, 这个异常在 ExceptionTranslationFilter里给catch住了, 随后的 handleException时, 由 sendStartAuthentication方法负责 new了一个SavedRequest 对象, 这个对象里正是装了刚才那个链接,随后把这个 SavedRequest对象放进了Session . 于是用户登录, 登录妥当后, AbstractProcessingFilter里的 successfulAuthentication方法出面通过 determineTargetUrl方法从session中取出前面放到session中的 SavedRequest对象, 最后再交由 sendRedirect方法, 把用户想要的东东呈现在了眼前.
说了半天了, SavedRequest抢去了大多数的风头, 不过也好, 把这个 SavedRequest研究透了后, SavedRequestAwareWrapper的功能就不攻自破了. 也就是说从历史地角度看清了SavedRequest对象的来龙去脉, SavedRequestAwareWrapper对象的现在问题也就应刃而解了.