shiro 一個項目多個系統sessionid賦值
Shiro Security是非常不錯的Security框架
最近在我的項目中進行相關整合,shiro不難,難就難在如何對已經成熟的系統進行整合
作為相關切入點,我也考慮了很久,整體運用上了如張開濤大佬所說
對於Subject我們一般這麼使用:
1、身份驗證(login)
2、授權(hasRole*/isPermitted*或checkRole*/checkPermission*)
3、將相應的數據存儲到會話(Session)
4、切換身份(RunAs)/多線程身份傳播
5、退出
回歸標題,正常整合過後,基本可以正確的進行登錄與登出
那麼開始進行細節休整
大體介紹我們的系統架構是springmvc進行開發,一個項目里分出了兩套系統,系統與系統間的區分僅僅只是 通過url路徑上的不同,來表現。那麼現在就出現了一種情況,系統1基本用戶都能登入,而系統2卻只有相關許可權人才能登入。
表面上視乎能在登陸上做控制,比如login的時候通過許可權判斷就可以做到。那麼這時候考慮的是如果用戶之間通過url強行進入呢。
比如系統1用戶登錄,直接修改url進入系統2。此時屬於非法訪問。
正常我們會在filter內做過濾,也好做,在相關登錄邏輯內對session賦予標記,在filter做過濾就over了,不符合直接logout。
想到這裡,作為一個shiro框架使用者是不是感覺shiro貌似沒起作用。
於是思考一個比較合理的方案,於是決定當用戶登陸系統時,對sessionid進行賦值,系統1則在sessionid前+上相關字元串標記session為系統1用戶。系統2則在sessionid前+上相關字元串標記session為系統2用戶。
需求清晰,那麼進行可行性分。
那麼結合shiro中的session管理,開始做起了調查。
首先進行是的shiro如何修改sessionid,這能找卻不能滿足我的需求。因為市面上的他們都是以單系統,或者雙項目雙系統進行寫的。完全不能符合我想要的。
一般都是這麼做的
<!-- 會話Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/> **
<!--設置Cookie名字,默認為JSESSIONID-->
<property name="name" value="WEBSID" />** </bean>
上面的解決方案是對不同項目進行不同的jsessionid名的配置
但我一個項目里怎麼可能出現兩個shiro,不符合我的要求
於是考慮shiro內置處理session我要做點手腳。
首先是查到了sessionid生成器
自定義了一個id生成器
public class SysSessionIdGenerator implements SessionIdGenerator {
@Override
public Serializable generateId(Session session) {
if(session.getAttribute("sysType")!=null){
return session.getAttribute("sysType").toString+"_"+UUID.randomUUID.toString;
}
return UUID.randomUUID.toString;
}
}
主要實現SessionIdGenerator generateId的方法。這裡可以看見我吧sysType加到uuid前面。
然後就是注入給shiro使用
配置需要加入
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<bean id="sessionIdGenerator" class="***.SysSessionIdGenerator"/>
sessionDAO 也要記得注入給 sessionManager 這裡我就不寫了
那麼問題又來了,邏輯上不上應該生成session的時候調用嗎,那麼shiro的session是在什麼時候生成的呢。
我翻了翻源碼
subject.getSession
這個get方法實現的
public Session getSession {
return getSession(true);
}
public Session getSession(boolean create) {
if (log.isTraceEnabled) {
log.trace("attempting to get session; create = " + create +
"; session is null = " + (this.session == null) +
"; session has id = " + (this.session != null && session.getId != null));
}
if (this.session == null && create) {
//added in 1.2:
if (!isSessionCreationEnabled) {
String msg = "Session creation has been disabled for the current subject. This exception indicates " +
"that there is either a programming error (using a session when it should never be " +
"used) or that Shiro"s configuration needs to be adjusted to allow Sessions to be created " +
"for the current Subject. See the " + DisabledSessionException.class.getName + " JavaDoc " +
"for more.";
throw new DisabledSessionException(msg);
}
log.trace("Starting session for host {}", getHost);
SessionContext sessionContext = createSessionContext;
Session session = this.securityManager.start(sessionContext);
this.session = decorate(session);
}
return this.session;
}
可以看出,當你不傳參數的時候默認進行調用ture,
簡單的 說當getsession(true)時,會判斷現在是否有session,如果沒有,則新生成一個,有則就用現有的。false則是如果沒有,就不生成了返回null。
發現一個問題,當我剛剛進入登入頁面的時候,此時shiro已經生成了一個session,於是在登陸校驗時候不會生成新的session了。於是考慮了各種辦法,比如登入的時候先logout一下等等,當然這些都叫做歪門邪道。後來發現了這麼一篇文章
我用了他的方法反正是沒成功,系統還變的有點混亂。
仔細一看他的文章中有這麼一段:
使用過程中發現Shiro在登錄之後不會生成新的Jessionid。這顯然會出現Session_Fixation。
Shiro自己說會在下一個版本1.3 fix這個問題。
我shiro起步是張開濤大大文章里的版本,所以是1.2.2的。嘗試性的換個版本,看了下官網的版本是1.3.2
先換了再說。
發現確實登陸之前與之後sessionid變了,看來在1.3.2的時候會在getsession重新獲得session。
但是這一點我並不明確,只能推測是這樣。
但是這還不是我的正道。重新明確技術細節,發現我需要重載getsession方法,在getsession的時候把sysTpye(系統標記)字元串傳遞進去。
還是剛剛上面的代碼有這麼一行
SessionContext sessionContext = createSessionContext;
Session session = this.securityManager.start(sessionContext);
它吧sessionContext傳遞進去創建了。那麼我似乎可以在這裡做文章,查閱資料後發現SessionContext繼承了Map。那麼我就可以直接對它進行put了。
那麼繼續往下挖掘源碼。
這篇挖掘的文章可以看看,我反正看完思路清晰了一點,畢竟自己debug比較混亂。
此時考慮到sessionContext對象還不是最終目標session,那麼我賦予的值要麼shiro會對其進行全部輸出到session里,要麼什麼也不做
public class SimpleSessionFactory implements SessionFactory {
/**
* Creates a new {@link SimpleSession SimpleSession} instance retaining the context"s
* {@link SessionContext#getHost host} if one can be found.
*
* @param initData the initialization data to be used during {@link Session} creation.
* @return a new {@link SimpleSession SimpleSession} instance
*/
public Session createSession(SessionContext initData) {
if (initData != null) {
String host = initData.getHost;
if (host != null) {
return new SimpleSession(host);
}
}
return new SimpleSession;
}
}
最後扒到這裡,它只是取了host 然後賦值,生成simplesession對象。
看來這裡是SessionContext的數據終點,那麼我systpye也得在這裡進行操作了。
查詢 SessionFactory相關資料後,發現原來我們自己也可以定義自己的SessionFactory對象。於是自定義了一個SessionFactory
public class HrsystemSessionFactory implements SessionFactory {
@Override
public Session createSession(SessionContext initData) {
Session session = null;
if (initData != null) {
String host = initData.getHost;
if (host != null) {
session = new SimpleSession(host);
}
if(initData.get("sysType")!=null){
session.setAttribute("sysType", initData.get("sysType"));
}
}else{
session = new SimpleSession;
}
return session;
}
}
這裡做的是把sysType的值賦值給session,然後配置文件注入。
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- session的失效時長,單位毫秒 -->
<property name="globalSessionTimeout" value="1800000"/>
<!-- 刪除失效的session -->
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionFactory" ref="sessionFactory"/>
<property name="sessionDAO" ref="sessionDAO"/>
</bean>
<bean id="sessionFactory" class="***.HrsystemSessionFactory"/>
那麼與剛剛的SysSessionIdGenerator對接上了。
接下來就是重頭戲,重載getsession;以上都有廢話之嫌,長話短說。
首先拓展suject介面
public interface SysSubject extends Subject {
Session getSession(String sysType);
}
這裡我直接繼承Subject介面;
public static Subject getSubject {
Subject subject = ThreadContext.getSubject;
if (subject == null) {
subject = (new Subject.Builder).buildSubject;
ThreadContext.bind(subject);
}
return subject;
}
Subject實例是使用ThreadLocal模式來獲取,若沒有則創建一個並綁定到當前線程。此時創建使用的是Subject內部類Builder來創建的,Builder會創建一個SubjectContext介面的實例DefaultSubjectContext,最終會委託securityManager來根據SubjectContext信息來創建一個Subject
上面代碼就是前面介紹源碼的文章里有講的。
那麼主要的就是需要進行實例化編寫了。
這裡有點回到原點了,從創建session變成了如何創建subject對象。
但是仔細剖析方法結構,會發現其實subject獲取模式與session獲取模式是一樣的。
但是重寫subjectFactory在網路與張開濤裡面都沒有觸及或者很少。這可能需要閱讀源碼才理解。
代碼如下
public class HrsystemSubjectFactory extends DefaultWebSubjectFactory {
public HrsystemSubjectFactory {
super;
}
public Subject createSubject(SubjectContext context) {
if (!(context instanceof WebSubjectContext)) {
return super.createSubject(context);
}
WebSubjectContext wsc = (WebSubjectContext) context;
SecurityManager securityManager = wsc.resolveSecurityManager;
Session session = wsc.resolveSession;
boolean sessionEnabled = wsc.isSessionCreationEnabled;
PrincipalCollection principals = wsc.resolvePrincipals;
boolean authenticated = wsc.resolveAuthenticated;
String host = wsc.resolveHost;
ServletRequest request = wsc.resolveServletRequest;
ServletResponse response = wsc.resolveServletResponse;
return new HrsystemSubject(principals, authenticated, host, session, sessionEnabled,
request, response, securityManager);
}
/**
* @deprecated since 1.2 - override {@link #createSubject(org.apache.shiro.subject.SubjectContext)} directly if you
* need to instantiate a custom {@link Subject} class.
*/
@Deprecated
protected Subject newSubjectInstance(PrincipalCollection principals, boolean authenticated,
String host, Session session,
ServletRequest request, ServletResponse response,
SecurityManager securityManager) {
return new WebDelegatingSubject(principals, authenticated, host, session, true,
request, response, securityManager);
}
}
主要是在createSubject方法中實例化HrsystemSubject對象將它傳遞出去。
而這個HrsystemSubject實例化SysSubject介面 與繼承WebDelegatingSubject對象
public class HrsystemSubject extends WebDelegatingSubject implements SysSubject{
public HrsystemSubject(PrincipalCollection principals, boolean authenticated,
String host, Session session, boolean sessionEnabled,
ServletRequest request, ServletResponse response,
SecurityManager securityManager) {
super(principals, authenticated, host, session, sessionEnabled, request, response, securityManager);
}
public Session getSession(String type) {
SessionContext sessionContext = createSessionContext;
sessionContext.put("sysType", type);
Session session = this.securityManager.start(sessionContext);
super.session = decorate(session);
return super.session;
}
}
ok那麼此時還沒完,我們需要把HrsystemSubjectFactory注入到shiro中去。
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="authenticator" ref="authenticator"></property>
<property name="subjectFactory" ref="subjectFactory"/>
<property name="realms">
<list>
<ref bean="hrRealm" />
<ref bean="bizRealm"/>
</list>
</property>
<property name="sessionManager" ref="sessionManager" />
</bean>
<bean id="subjectFactory" class="***HrsystemSubjectFactory"/>
這裡我完全是模仿著session套路感覺注入的了,因為並沒有文章這麼做。(或者我沒看到吧)
於是此時SecurityUtils.getSubject;get的出來的對象就是我們的HrsystemSubject了。
但是這裡運用了下父子繼承原理,get對象實際是Subject,內部對象的實例其實是HrsystemSubject
那麼我們在對其強制裝換成我們剛剛定的(SysSubject)SecurityUtils.getSubject;
於是關於getsession的重載就完成了。
那麼邏輯上輸入getsession(string sysType)那麼就可以對sessionid進行我想要的值了。也能做邏輯控制了。運用場景還是挺廣的。


※[Android]Android焦點流程代碼分析
※RabbitMQ 使用場景一
※python爬蟲——自動獲取seebug的poc
※vue-resource pos提交t數據時碰到Django csrf
※Go語言學習筆記(六)net
TAG:達人科技 |
※blogfoster-scripts:一款簡化 Node.js 項目初始化的工具
※Origin Protocol項目短評
※如何在Microsoft Translator中越權刪除1.3萬個項目
※All You Need Is Light:Olafur Eliasson的小太陽項目
※專業解讀 Business Analytics項目
※Mozilla期望Firefox Fission項目能瓦解Spectre漏洞攻擊
※flask 項目中使用 bootstrapFileInput
※Jason Rubin:Oculus團隊中有多個價值數百萬美元的Rift項目
※使用Visual Studio Code編譯、調試Apollo項目
※區塊鏈項目 Dfinity 獲得由 Andreessen Horowitz 和 Polychain 領投的 6100 萬美元融資
※使用Skaffold一鍵將項目發布到Kubernetes
※軟體定義網路項目OpenContrail改名為Tungsten Fabric
※Andromeda項目:Surface Phone或以另外形式亮相
※使用Centos7.5+Nginx+Gunicorn+Django+Python3部署blog項目
※springboot項目初始化
※Blazor正式成為Microsoft官方.NET 和WebAssembly項目
※Android Studio項目模板全面解析
※GitHub 熱門項目:Python Fire
※Kickstarter升級Hardware Studio:讓投資者知道眾籌項目發展進度
※kindeditor項目集成