Liferay中如何在Portal级别的Session中放入属性然后在Portlet中使用

引入:

我们有以下一个需求,比如我们在用户登录模块,登录成功后我们会得到一个authToken, 然后我们希望吧这个authToken存放在Portal级别的Session中,然后在我们的指定的Portlet中从Portal级别的Session中使用这个Session,那么如何才能做到呢?


分析:

很显然,我们在用户登录中[项目1:一个LoginHook](比如我们用Struts2做了一个登录Action),因为我们能访问到的是HttpServletRequest,所以我们的代码是显而易见的

在Action中我们设置属性到Session上:

103359664.png

然后我们在项目2:Portlet上拿属性,因为我们我们要Session是PortletSession,所以scope设置的是PortletSession.APPLICATION_SCOPE (这个如何不明白可以参见http://supercharles888.blog.51cto.com/609344/1297791)

103651551.png

然后我们想,只要属性的key一样就可以了,所以在用户登录模块中设置key为 ”authToken",在Portlet中取key为"authToken",原来信心满满的以为肯定能取到,结果调试一下,这个Portlet第106行居然拿到的authToken为null。奇怪了,我们明明放上去了啊,肯定是忽略了什么?



解决:

这问题其实花了我1个多小时才解决,后来才发现,我们的session逻辑分为2部分,一个是portalSession,一个是portletSession,他们共同封装在类SharedSessionWrapper中。

110254599.png

如果我们要从session上取值的时候,这个值来自portalSession还是portletSession和authToken的取值有关,这个key必须满足指定的前缀,才能保证这个值是从portalSession中取来的。


先讲调试成功的例子:

我们假设这authToken取值是以LIFERAY_SHARED_前缀开头,比如叫LIFERAY_SHARED_GeneratedAuthToken, 那么在Portlet 106行,调试session.getAttribute的时候,它的session来自于getSessionDelegate()方法:

104357899.png

而这个getSessionDelegate()方法定义在SharedSessionWrapper类中,它的逻辑如下:

104542787.png


从这里可以看出,session的取值可以是portalSession,也可以是portletSession,但是遵守如下原则:

(1)如果portletSession不存在,那么就返回portalSession

(2)如果我们共享属性的key属于sharedSessionAttributesExcludes列表,即被共享Session排除在外在外的属性,那么session返回portletSession.

(3)如果我们的属性key属于sharedAttribute列表,那么session返回portalSession.

(4)默认情况,返回portletSession.


所以这里出现了2个很有趣的名词,一个叫sharedSessionAttributesExcludes,这个属性的作用是定义了一组列表,然后列表中的所有属性都不可以作为session的共享属性的,而我们知道portal级别的session是用于共享属性的,所以在这个列表中定义的属性都不可以放在portalSession上。

它的定义在portal.properties中:

105159573.png

所以我们知道,默认上liferay是不让用户名密码从portal级别共享到portlet中的,如果我们希望一些其他属性不想从portal级别共享到portlet级别,我们可以在portal-ext.properties中覆盖这个属性。


另外一个有趣名词叫sharedAttribute,显然,这个应该和上述相反的作用,是定义希望从portal级别共享到portlet级别的属性,从而一旦这种属性的key满足条件,那么他们就应该位于portalSession中从而方便共享。


我们回到代码197行中看出containsSharedAttribute(name)的实现,也就是满足何种条件的属性被视为共享属性(portal到portlet级别的共享)

105639912.png

很容易我们找到了答案,它会去判断我们的属性name是否以session.shared.attributes中定义的某个前缀为前缀,而这个session.shared.attributes同样定义在portal.properties中:

105850292.png

回到我们的例子中,因为我们定义的属性的值为LIFERAY_SHARED_GeneratedAuthToken,而LIFERAY_SHARED_是一个被用于session共享属性的前缀,所以符合要求,所以我们的containsSharedAttribute(name)返回true,从而最后我们getSessionDelegate返回的是portalSession,

110143444.png

所以在portalSession中获取我们存放在portal级别的session上的属性,当然成功了。

111548218.png


而回到我们开始"分析“中给出的例子,假设我们随随便便指定一个属性的key,比如就叫"authToken",因为他们不满足任何的session.shared.attributes中定义的前缀,所以默认的,根据我们getSessionDelegate()的最后一个分支,它返回的是portletSession,而我们在登录模块中,是吧属性存放在全局的portalSession中的,当然你不可能再portletSession中根据名字获取到相应的属性值,这就是为什么开始我们得到的authToken为null的原因。



总结:

(1)在一个Liferay环境中,从Portlet的视角看,它总是对应这2个Session,一个是portalSession,一个是portletSession,并且这个都封装在SharedSessionWrapper中.

(2)当共享属性变量时,如何判断是从portalSession中拿属性还是从portletSession中拿属性来自于getSessionDelegate()方法,它取决于session存在性以及属性key的取值,具体逻辑如下:

a.如果portletSession不存在,那么就返回portalSession

b.如果我们共享属性的key属于sharedSessionAttributesExcludes列表,即被共享Session排除在外在外的属性,那么session返回portletSession.

c.如果我们的属性key属于sharedAttribute列表,那么session返回portalSession.

d.默认情况,返回portletSession.

(3)如果希望某个属性要从portalSession中拿, 那么它的前缀必须满足portal.properties中session.shared.attributes中定义的前缀列表,你可以可以覆写portal.properties中此属性来自定义前缀列表。

(4)如果希望某个属性一定不从portalSession中拿,那么可以吧这个属性的值加到session.shared.attributes.excludes列表中。


你可能感兴趣的:(PortletSession,共享属性,portalSession)