spring mvc下shiro的session,request等問題


最近的一個項目使用的是spring mvc,權限框架使用的是shiro.

不過有一個問題一直困擾着我,現在的session到底是誰的session,是servlet的還是shiro的.

於是我把spring controller參數里面的HttpServletRequest對象和HttpSession對象打印了出來

這兩個對象打印的結果是org.apache.shiro.web.servlet.ShiroHttpServletRequest和org.apache.shiro.web.servlet.ShiroHttpSession

在不使用shiro時這些對象應該均為tomcat所實現的類,這說明在shiro執行filter時將request對象包裝成了shiro實現的類.

那么shiro是在什么時候將request對象包裝了的呢?

先看一下shiro的攔截器類圖:

ShiroFilter 是整個 Shiro 的入口點,用於攔截需要安全控制的請求進行處理。ShiroFilter 繼承自AbstractShiroFilter,而AbstractShiroFilter繼承自OncePerRequestFilter

先看一下OncePerRequestFilter的源碼 :

public abstract class OncePerRequestFilter extends NameableFilter {

 

    // 已過濾屬性的后綴名

    public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

 

    // 是否開啟過濾功能

    private boolean enabled = true;

 

    public boolean isEnabled() {

        return enabled;

    }

 

    public void setEnabled(boolean enabled) {

        this.enabled = enabled;

    }

 

    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // 獲取 Filter 已過濾的屬性名

        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();

        // 判斷是否已過濾

        if (request.getAttribute(alreadyFilteredAttributeName) != null) {

            // 若已過濾,則進入 FilterChain 中下一個 Filter

            filterChain.doFilter(request, response);

        } else {

            // 若未過濾,則判斷是否未開啟過濾功能(其中 shouldNotFilter 方法將被廢棄,由 isEnabled 方法取代)

            if (!isEnabled(request, response) || shouldNotFilter(request)) {

                // 若未開啟,則進入 FilterChain 中下一個 Filter

                filterChain.doFilter(request, response);

            } else {

                // 若已開啟,則將已過濾屬性設置為 true(只要保證 Request 中有這個屬性即可)

                request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

                try {

                    // 在子類中執行具體的過濾操作

                    doFilterInternal(request, response, filterChain);

                } finally {

                    // 當前 Filter 執行結束需移除 Request 中的已過濾屬性

                    request.removeAttribute(alreadyFilteredAttributeName);

                }

            }

        }

    }

 

    protected String getAlreadyFilteredAttributeName() {

        String name = getName();

        if (name == null) {

            name = getClass().getName();

        }

        return name + ALREADY_FILTERED_SUFFIX;

    }

 protected boolean isEnabled(ServletRequest request, ServletResponse response) throws ServletException, IOException {

        return isEnabled();

    }

 protected boolean shouldNotFilter(ServletRequest request) throws ServletException {

        return false;

    }

 

    protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException;

}

我們看到OncePerRequestFilter里面實現了Servlet規范的doFilter(),並且將該方法聲明為final,可以看出shiro不允許其子類再復寫該方法.

但OncePerRequestFilter並沒有實現 doFilterInternal(request, response, filterChain),這說明該方法是需要在子類中實現的.

再來看一下OncePerRequestFilter的子類AbstractShiroFilter:

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain) throws ServletException, IOException {

        Throwable t = null;

        try {

            // 返回被 Shiro 包裝過的 Request 與 Response 對象

            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);

            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

            // 創建 Shiro 的 Subject 對象

            final Subject subject = createSubject(request, response);

            // 使用異步的方式執行相關操作

            subject.execute(new Callable() {

                public Object call() throws Exception {

                    // 更新 Session 的最后訪問時間

                    updateSessionLastAccessTime(request, response);

                    // 執行 Shiro 的 Filter Chain

                    executeChain(request, response, chain);

                    return null;

                }

            });

        } catch (ExecutionException ex) {

            t = ex.getCause();

        } catch (Throwable throwable) {

            t = throwable;

        }

        if (t != null) {

            if (t instanceof ServletException) {

                throw (ServletException) t;

            }

            if (t instanceof IOException) {

                throw (IOException) t;

            }

            throw new ServletException(t);

        }

    }

果然,在子類AbstractShiroFilter中實現了doFilterInternal()方法.

其中用於包裝request的函數:

protected ServletRequest prepareServletRequest(ServletRequest request, ServletResponse response, FilterChain chain) {
        ServletRequest toUse = request;
        if(request instanceof HttpServletRequest) {
            HttpServletRequest http = (HttpServletRequest)request;
            toUse = this.wrapServletRequest(http);
        }

        return toUse;
    }
protected ServletRequest wrapServletRequest(HttpServletRequest orig) {
        return new ShiroHttpServletRequest(orig, this.getServletContext(), this.isHttpSessions());
    }

現在終於知道shiro是怎么把request對象包裝成ShiroHttpServletRequest類型的了.

一開始session的困惑也能解開了:

因為session是通過request獲取的,所以先看一下ShiroHttpServletRequest獲取session的源碼:

public HttpSession getSession(boolean create) {
        HttpSession httpSession;
        if(this.isHttpSessions()) {
            httpSession = super.getSession(false);
            if(httpSession == null && create) {
                if(!WebUtils._isSessionCreationEnabled(this)) {
                    throw this.newNoSessionCreationException();
                }

                httpSession = super.getSession(create);
            }
        } else {
            if(this.session == null) {
                boolean existing = this.getSubject().getSession(false) != null;
                Session shiroSession = this.getSubject().getSession(create);
                if(shiroSession != null) {
                    this.session = new ShiroHttpSession(shiroSession, this, this.servletContext);
                    if(!existing) {
                        this.setAttribute(REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
                    }
                }
            }

            httpSession = this.session;
        }

        return httpSession;
    }

這里主要講的是:

如果this.isHttpSessions()返回true,則返回父類HttpServletRequestWrapper的

也就是servelet規范的session,否則返回ShiroHttpSession對象.

那么this.isHttpSessions()是什么呢?

 
         
protected boolean httpSessions = true;

public
boolean isHttpSessions() { return this.httpSessions; }

this.isHttpSessions()返回ShiroHttpServletRequest對象的一個屬性值,默認是true.

ShiroHttpServletRequest的構造函數是這樣的:

public ShiroHttpServletRequest(HttpServletRequest wrapped, ServletContext servletContext, boolean httpSessions) {
        super(wrapped);
        this.servletContext = servletContext;
        this.httpSessions = httpSessions;
    }

這說明在ShiroHttpServletRequest對象生成之初就必須指定httpSessions的值.

再回到剛才shiro包裝request的地方.

protected ServletRequest wrapServletRequest(HttpServletRequest orig) {
        return new ShiroHttpServletRequest(orig, this.getServletContext(), this.isHttpSessions());
    }
protected boolean isHttpSessions() {
        return this.getSecurityManager().isHttpSessionMode();
    }

這里的this.isHttpSessions()取決於this.getSecurityManager().isHttpSessionMode()的值.

我們項目里SecurityManager設置為DefaultWebSecurityManager.其中isHttpSessionMode()方法為:

public boolean isHttpSessionMode() {
        SessionManager sessionManager = this.getSessionManager();
        return sessionManager instanceof WebSessionManager && ((WebSessionManager)sessionManager).isServletContainerSessions();
    }

我們這個項目使用的sessionManager是DefaultWebSessionManager,DefaultWebSessionManager實現了sessionManager 接口.

但是DefaultWebSessionManager中該方法返回的是false.

public boolean isServletContainerSessions() {
        return false;
    }

所以最終session得到的是ShiroHttpSession.

轉載請注明出處http://www.cnblogs.com/vinozly/p/5080692.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM