您的位置:首頁>正文

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, 這能找卻不能滿足我的需求。 因為市面上的他們都是以單系統, 或者雙專案雙系統進行寫的。 完全不能符合我想要的。

一般都是這麼做的

**

**

上面的解決方案是對不同專案進行不同的jsessionid名的配置

但我一個專案裡怎麼可能出現兩個shiro, 不符合我的要求

於是考慮shiro內置處理session我要做點手腳。

首先是查到了sessionid生成器

自訂了一個id生成器

public class SysSessionIdGenerator implements SessionIdGenerator {@Overridepublic 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使用

配置需要加入

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 {@Overridepublic 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, 然後設定檔注入。

那麼與剛剛的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中去。

這裡我完全是模仿著session套路感覺注入的了,因為並沒有文章這麼做。(或者我沒看到吧)

於是此時SecurityUtils.getSubject;get的出來的物件就是我們的HrsystemSubject了。

但是這裡運用了下父子繼承原理,get物件實際是Subject,內建物件的實例其實是HrsystemSubject

那麼我們在對其強制裝換成我們剛剛定的(SysSubject)SecurityUtils.getSubject;

於是關於getsession的重載就完成了。

那麼邏輯上輸入getsession(string sysType)那麼就可以對sessionid進行我想要的值了。也能做邏輯控制了。運用場景還是挺廣的。

這裡有點回到原點了,從創建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中去。

這裡我完全是模仿著session套路感覺注入的了,因為並沒有文章這麼做。(或者我沒看到吧)

於是此時SecurityUtils.getSubject;get的出來的物件就是我們的HrsystemSubject了。

但是這裡運用了下父子繼承原理,get物件實際是Subject,內建物件的實例其實是HrsystemSubject

那麼我們在對其強制裝換成我們剛剛定的(SysSubject)SecurityUtils.getSubject;

於是關於getsession的重載就完成了。

那麼邏輯上輸入getsession(string sysType)那麼就可以對sessionid進行我想要的值了。也能做邏輯控制了。運用場景還是挺廣的。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示