1. 程式人生 > >shiro原始碼分析篇2:請求過濾,登入判斷

shiro原始碼分析篇2:請求過濾,登入判斷

下面是我的配置檔案

spring-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
>
<!-- 快取管理器 使用Ehcache實現 --> <bean id="cacheManagerShiro" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> </bean> <!-- 憑證匹配器 --> <bean id="credentialsMatcher"
class="com.share1024.shiro.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManagerShiro"/> <property name="hashAlgorithmName" value="md5"/> <property name="hashIterations" value="2"/> <property name="storedCredentialsHexEncoded" value
="true"/>
</bean> <!-- Realm實現 --> <bean id="userRealm" class="com.share1024.shiro.UserRealm"> <property name="credentialsMatcher" ref="credentialsMatcher"/> <property name="cachingEnabled" value="true"/> <property name="authenticationCachingEnabled" value="true"/> <property name="authenticationCacheName" value="authenticationCache"/> <property name="authorizationCachingEnabled" value="true"/> <property name="authorizationCacheName" value="authorizationCache"/> </bean> <!-- 會話ID生成器 --> <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> <!-- 會話Cookie模板 --> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="sid"/> <property name="httpOnly" value="true"/> <property name="maxAge" value="6000000"/> </bean> <!-- 會話DAO --> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/> <property name="sessionIdGenerator" ref="sessionIdGenerator"/> </bean> <!-- 會話驗證排程器 --> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> <property name="sessionValidationInterval" value="1800000"/> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- 會話管理器 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <property name="globalSessionTimeout" value="1800000"/> <property name="deleteInvalidSessions" value="true"/> <property name="sessionValidationSchedulerEnabled" value="true"/> <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> <property name="sessionDAO" ref="sessionDAO"/> <property name="sessionIdCookieEnabled" value="true"/> <property name="sessionIdCookie" ref="sessionIdCookie"/> </bean> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm"/> <property name="sessionManager" ref="sessionManager"/> <property name="cacheManager" ref="cacheManagerShiro"/> </bean> <!-- 相當於呼叫SecurityUtils.setSecurityManager(securityManager) --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/> <property name="arguments" ref="securityManager"/> </bean> <!-- Shiro的Web過濾器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <!-- 要求登入時的連結 --> <property name="loginUrl" value="/login" /> <!-- 使用者訪問未對其授權的資源時,所顯示的連線 --> <property name="unauthorizedUrl" value="/forbidden" /> <property name="filterChainDefinitions"> <value> /=user /admin/**=user /admin=user /admin.html=user </value> </property> </bean> <!-- Shiro生命週期處理器--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> </beans>

在spring容器啟動時會初始化以上配置,實現依賴注入。相關bean初始化到spring容器中。

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>


  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:spring-shiro.xml</param-value>
  </context-param>
  <!-- shiro 安全過濾器 -->
  <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
  <!-- requests.  Usually this filter mapping is defined first (before all others) to -->
  <!-- ensure that Shiro works in subsequent filters in the filter chain:             -->
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>


  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>

</web-app>

web.xml載入順序為: context-param -> listener -> filter -> servlet
伺服器啟動時,先初始化了spring-shiro.xml,讓後再初始化攔截器。

程式碼:https://github.com/smallleaf/cacheWeb

斷點打到Filter init()方法,我們啟動伺服器一步步來。

DelegatingFilterProxy類

public class DelegatingFilterProxy extends GenericFilterBean 
public abstract class GenericFilterBean implements
    Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean

Filter init()方法的實現是GenericFilterBean實現的

public final void init(FilterConfig filterConfig) throws ServletException {
        Assert.notNull(filterConfig, "FilterConfig must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
        }

        this.filterConfig = filterConfig;

        // Set bean properties from init parameters.
        try {
            PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            String msg = "Failed to set bean properties on filter '" +
                filterConfig.getFilterName() + "': " + ex.getMessage();
            logger.error(msg, ex);
            throw new NestedServletException(msg, ex);
        }

        // Let subclasses do whatever initialization they like.
        initFilterBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
        }
    }

先初始化一些引數web.xml裡面

 <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>

我們具體看如何初始化這個攔截器bean

protected void initFilterBean() throws ServletException {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                // If no target bean name specified, use filter name.
                if (this.targetBeanName == null) {
                    this.targetBeanName = getFilterName();
                }
                // Fetch Spring root application context and initialize the delegate early,
                // if possible. If the root application context will be started after this
                // filter proxy, we'll have to resort to lazy initialization.
                WebApplicationContext wac = findWebApplicationContext();
                if (wac != null) {
                    this.delegate = initDelegate(wac);
                }
            }
        }
    }

先獲得filterName,這個filterName就是我們攔截器的名稱也就是shiroFilter。
獲得spring容器,我們看WebApplicationContext wac = findWebApplicationContext();上面的註釋:

protected WebApplicationContext findWebApplicationContext() {
        if (this.webApplicationContext != null) {
            // The user has injected a context at construction time -> use it...
            if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
                ConfigurableApplicationContext cac = (ConfigurableApplicationContext) this.webApplicationContext;
                if (!cac.isActive()) {
                    // The context has not yet been refreshed -> do so before returning it...
                    cac.refresh();
                }
            }
            return this.webApplicationContext;
        }
        String attrName = getContextAttribute();
        if (attrName != null) {
            return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
        }
        else {
            return WebApplicationContextUtils.findWebApplicationContext(getServletContext());
        }
    }

我們第一次初始化filter時,該webApplicationContext為null,我們從ServletContext上下文中去查詢這個容器。

protected final ServletContext getServletContext() {
        return (this.filterConfig != null ? this.filterConfig.getServletContext() : this.servletContext);
    }

由於GenericFilterBean實現了ServletContextAware,那麼GenericFilterBean已經具備了ServletContext這個能力。相信大家已經知道Aware介面就是讓我們的bean具備spring容器的感知能力。什麼意思呢?GenericFilterBean繼承ServletContextAware也就是要實現了:

public final void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

this.servletContext = servletContext;在該類進行依賴注入的時候,就將servletContext賦予給了GenericFilterBean.servletContext。這樣GenericFilterBean的servletContext已經被賦予值,是不是就具備這個能力了。

public static WebApplicationContext findWebApplicationContext(ServletContext sc) {
        WebApplicationContext wac = getWebApplicationContext(sc);
        if (wac == null) {
            Enumeration<String> attrNames = sc.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = attrNames.nextElement();
                Object attrValue = sc.getAttribute(attrName);
                if (attrValue instanceof WebApplicationContext) {
                    if (wac != null) {
                        throw new IllegalStateException("No unique WebApplicationContext found: more than one " +
                                "DispatcherServlet registered with publishContext=true?");
                    }
                    wac = (WebApplicationContext) attrValue;
                }
            }
        }
        return wac;
    }

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    }

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName() + “.ROOT”;上面就是要獲得我們root webApplicationContext。再回到上面

WebApplicationContext wac = findWebApplicationContext();
                if (wac != null) {
                    this.delegate = initDelegate(wac);
                }

    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }

從web容器中初始化我們代理的filter類。此時就是要準備初始化我們的shiro過濾器了。也就是org.apache.shiro.spring.web.ShiroFilterFactoryBean。
getTargetBeanName()就是shiroFilter
是我們web.xml中配置的過濾器名稱。然後根據該過濾器從spring容器當中取該類。targetFilterLifecycle web.xml中配置為true。表示filter的初始化工作交給這個DelegatingFilterProxy代理類來完成。

此時delegate.init(getFilterConfig());跳轉到:AbstractFilter中的init方法。此時就到了我們shiro ShiroFilterFactoryBean真正初始化的時候了。

具體的初始化過程就不看了。進入我們今天的重點。

瀏覽器輸入http://localhost:8080

同理斷點我們打到我們代理類DelegatingFilterProxy.doFilter方法中

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

        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized (this.delegateMonitor) {
                if (this.delegate == null) {
                    WebApplicationContext wac = findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    this.delegate = initDelegate(wac);
                }
                delegateToUse = this.delegate;
            }
        }

        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
    }

先判斷filter有沒有初始化,沒有初始化就現在初始化。
然後呼叫正在的doFilter方法。也就是OncePerRequestFilter.doFilter

public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor

因為ShiroFilterFactoryBean 實現了FactoryBean,在springbean 進行依賴注入時getBean,此時會呼叫ShiroFilterFactoryBean.getObject的方法。

    public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }
protected AbstractShiroFilter createInstance() throws Exception {

        log.debug("Creating Shiro Filter instance.");

        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }

        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }

        FilterChainManager manager = createFilterChainManager();

        //Expose the constructed FilterChainManager by first wrapping it in a
        // FilterChainResolver implementation. The AbstractShiroFilter implementations
        // do not know about FilterChainManagers - only resolvers:
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);

        //Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
        //FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class
        //here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
        //injection of the SecurityManager and FilterChainResolver:
        return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

返回真正的過濾類為:SpringShiroFilter

private static final class SpringShiroFilter extends AbstractShiroFilter 
public abstract class AbstractShiroFilter extends OncePerRequestFilter
public abstract class OncePerRequestFilter extends NameableFilter
public abstract class NameableFilter extends AbstractFilter implements Nameable
public abstract class AbstractFilter extends ServletContextSupport implements Filter

可以看出SpringShiroFilter實現了OncePerRequestFilter,那麼呼叫OncePerRequestFilter.doFilter也就是再執行我們的shiro過濾器了。

public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
        if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
            log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());
            filterChain.doFilter(request, response);
        } else //noinspection deprecation
            if (/* added in 1.2: */ !isEnabled(request, response) ||
                /* retain backwards compatibility: */ shouldNotFilter(request) ) {
            log.debug("Filter '{}' is not enabled for the current request.  Proceeding without invoking this filter.",
                    getName());
            filterChain.doFilter(request, response);
        } else {
            // Do invoke this filter...
            log.trace("Filter '{}' not yet executed.  Executing now.", getName());
            request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

            try {
                doFilterInternal(request, response, filterChain);
            } finally {
                // Once the request has finished, we're done and we don't
                // need to mark as 'already filtered' any more.
                request.removeAttribute(alreadyFilteredAttributeName);
            }
        }
    }

先判斷這個filter有沒有過濾,有就跳過執行過濾鏈,沒有就過濾。
進入AbstractShiroFilter.doFilterInternal

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

        Throwable t = null;

        try {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

            final Subject subject = createSubject(request, response);

            //noinspection unchecked
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    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;
            }
            //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:
            String msg = "Filtered request failed.";
            throw new ServletException(msg, t);
        }
    }

重點createSubject。
subject:就是一個主體,一個使用者。與伺服器進行與互動。帶著一些自己的資訊,比如session,許可權,認證資訊等等。正在進行互動的是securityManager。跟下去發現進入:DefaultSecurityManager.createSubject

  public Subject createSubject(SubjectContext subjectContext) {
        //create a copy so we don't modify the argument's backing map:
        SubjectContext context = copy(subjectContext);

        //ensure that the context has a SecurityManager instance, and if not, add one:
        context = ensureSecurityManager(context);

        //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before
        //sending to the SubjectFactory.  The SubjectFactory should not need to know how to acquire sessions as the
        //process is often environment specific - better to shield the SF from these details:
        context = resolveSession(context);

        //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first
        //if possible before handing off to the SubjectFactory:
        context = resolvePrincipals(context);

        Subject subject = doCreateSubject(context);

        //save this subject for future reference if necessary:
        //(this is needed here in case rememberMe principals were resolved and they need to be stored in the
        //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).
        //Added in 1.2:
        save(subject);
        return subject;
    }

看英文和方法名稱大概就知道什麼意思。

此時回到最初我們的目的是要判斷shiro是如何判斷使用者沒有登入,攔截這個請求到登入頁面的。
context = resolveSession(context);這個是用來建立session的。

  Session session = resolveContextSession(context);
 protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {
        SessionKey key = getSessionKey(context);
        if (key != null) {
            return getSession(key);
        }
        return null;
    }
public Session getSession(SessionKey key) throws SessionException {
        Session session = lookupSession(key);
        return session != null ? createExposedSession(session, key) : null;
    }
     protected final Session doGetSession(final SessionKey key) throws InvalidSessionException {
        enableSessionValidationIfNecessary();

        log.trace("Attempting to retrieve session with key {}", key);

        Session s = retrieveSession(key);
        if (s != null) {
            validate(s, key);
        }
        return s;
    }
     public Serializable getSessionId(SessionKey key) {
        Serializable id = super.getSessionId(key);
        if (id == null && WebUtils.isWeb(key)) {
            ServletRequest request = WebUtils.getRequest(key);
            ServletResponse response = WebUtils.getResponse(key);
            id = getSessionId(request, response);
        }
        return id;
    }
  private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
        if (!isSessionIdCookieEnabled()) {
            log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie.");
            return null;
        }
        if (!(request instanceof HttpServletRequest)) {
            log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie.  Returning null.");
            return null;
        }
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
    }

getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));這個就是從我們瀏覽器帶過來的cookie中的sessionId.

 Session s = retrieveSessionFromDataSource(sessionId);
    protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
        return sessionDAO.readSession(sessionId);
    }
CachingSessionDao類
  public Session readSession(Serializable sessionId) throws UnknownSessionException {
        Session s = getCachedSession(sessionId);
        if (s == null) {
            s = super.readSession(sessionId);
        }
        return s;
    }
    protected Session getCachedSession(Serializable sessionId) {
        Session cached = null;
        if (sessionId != null) {
            Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
            if (cache != null) {
                cached = getCachedSession(sessionId, cache);
            }
        }
        return cached;
    }
   意思是先獲取快取這個快取就是上面配置的ehcache快取。
   通過SessionId獲得快取。

獲得session後呼叫context.setSession(session);
以上就完成了Session的獲取。

那session是如何儲存的呢?第一次請求伺服器的時候,是沒有session的伺服器返回一個session。以後瀏覽器每次請求都帶上這個Session。這個第一次session是怎麼儲存的呢?

咋們接著走:context = resolvePrincipals(context);

public PrincipalCollection resolvePrincipals() {
        PrincipalCollection principals = getPrincipals();

        if (CollectionUtils.isEmpty(principals)) {
            //check to see if they were just authenticated:
            AuthenticationInfo info = getAuthenticationInfo();
            if (info != null) {
                principals = info.getPrincipals();
            }
        }

        if (CollectionUtils.isEmpty(principals)) {
            Subject subject = getSubject();
            if (subject != null) {
                principals = subject.getPrincipals();
            }
        }

        if (CollectionUtils.isEmpty(principals)) {
            //try the session:
            Session session = resolveSession();
            if (session != null) {
                principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);
            }
        }

        return principals;
    }

獲取你有沒有登入的Principal資訊。顯然null。登入認證AuthenticationInfo也null,session也null。
Subject subject = doCreateSubject(context); save()。就不分析了,按字面意思理解即可。

接下來看:

subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    executeChain(request, response, chain);
                    return null;
                }
            });

   protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        FilterChain chain = getExecutionChain(request, response, origChain);
        chain.doFilter(request, response);
    }

做好了subject準備工作了。那麼開始進行攔截處理了。
executeChain這個方法是處理的方法。
我們先獲取處理的類FilterChain。我們攔截的請求 /=user。就是通過user來取對應的FilterChain。當然這個我們也可以自定義。

public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);

這些處理類就是在ShiroFilterFactoryBean依賴注入時初始化的。
user那麼對應的就是UserFilter

public class UserFilter extends AccessControlFilter 
public abstract class AccessControlFilter extends PathMatchingFilter
public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor
public abstract class AdviceFilter extends OncePerRequestFilter
public abstract class OncePerRequestFilter extends NameableFilter 
public abstract class NameableFilter extends AbstractFilter implements Nameable

接著走

ProxiedFIlterChain.doFilter
OncePerRequestFilter.doFIlter
AdviceFilter.doFilterInternal
    public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        Exception exception = null;

        try {

            boolean continueChain = preHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Invoked preHandle method.  Continuing chain?: [" + continueChain + "]");
            }

            if (continueChain) {
                executeChain(request, response, chain);
            }

            postHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Successfully invoked postHandle method");
            }

        } catch (Exception e) {
            exception = e;
        } finally {
            cleanup(request, response, exception);
        }
    }
 boolean continueChain = preHandle(request, response);
  private boolean isFilterChainContinued
   return onPreHandle(request, response, pathConfig);

    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
    }

通過登入的連結攔截到後,執行對應過濾器的過濾方法。isAccessAllowed判斷是否登入,就到達請求的頁面,沒有就執行onAccessDenied跳轉到登入頁面。

   protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginRequest(request, response)) {
            return true;
        } else {
            Subject subject = getSubject(request, response);
            
            
           

相關推薦

shiro原始碼分析2:請求過濾登入判斷

下面是我的配置檔案 spring-shiro.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"

shiro原始碼分析5:結合redis實現session跨域

相信大家對session跨域也比較瞭解了。以前單臺伺服器session本地快取就可以了,現在分散式後,session集中管理,那麼用redis來管理是一個非常不錯的選擇。 在結合redis做session快取的時候,也遇到了很多坑,不過還算是解決了。 和上篇講述一樣,實現自定義快

shiro原始碼分析4:自定義快取

這篇講解shiro如何管理session,如何與ehcache結合。我們自己如何寫個簡單的快取替換ehcache。 首先來看看配置 <!-- 快取管理器 使用Ehcache實現 --> <bean id="cacheManagerShiro" cla

shiro原始碼分析3:使用者登入快取登入資訊

上篇講了shiro是如何過濾請求連結,判斷使用者是否已經登入。 這篇就是講解shiro使用者登入時,如何把登入資訊快取起來,下次使用者登入其他需要登入的連結時,如何判斷已經登入了。 RetryLimitHashedCredentialsMatcher自定義的登入憑據,也就是登入的

shiro原始碼分析1:前言

目的:通過這幾篇shiro原始碼分析,用redis代替ehache做session快取。 相信大家對shiro也不陌生了,網上對shiro原始碼分析的也比較多了。筆者也看多很多優秀的部落格。這幾篇shiro原始碼分析的目的,就是弄明白,shiro是如何管理session的,如何通過r

Shiro原始碼分析(2) - 會話管理器(SessionManager)

本文在於分析Shiro原始碼,對於新學習的朋友可以參考 [開濤部落格](http://jinnianshilongnian.iteye.com/blog/2018398)進行學習。 本文對Shiro中的SessionManager進行分析,SessionMan

原始碼分析--Java集合操作(2

4、兩大集合介面 在Java集合中,有兩大集合,一個是Collection介面及其實現類,另一個是Map介面及其實現類。下面給出這兩種集合的框架圖。如下所示。 4.1Collection介面框架圖 4.2Map介面框架圖 從上面兩個框架圖可以看出,Cllection介面和Map介面是

Mybatis 原始碼分析2)—— 引數處理

Mybatis對引數的處理是值得推敲的,不然在使用的過程中對發生的一系列錯誤直接懵逼了。 以前遇到引數繫結相關的錯誤我就是直接給加@param註解,也稀裡糊塗地解決了,但是後來遇到了一些問題推翻了我的假設:單個引數不需要使用 @param 。由此產生了一個疑問,Mybatis到底是怎

PackageManagerService 原始碼分析2

  一.scanPackageLI PKMS 中呼叫scanDirLI來分析APK 檔案,如果目錄下的是apk檔案或者是目錄,會繼續呼叫scanPackageLI函式: private PackageParser.Package scanPackageLI(File s

Shiro原始碼分析(3) - 認證器(Authenticator)

本文在於分析Shiro原始碼,對於新學習的朋友可以參考 [開濤部落格](http://jinnianshilongnian.iteye.com/blog/2018398)進行學習。 Authenticator就是認證器,在Shiro中負責認證使用者提交的資訊,

Netty原始碼分析:1.2初始化NioEventLoop

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

原始碼分析之手寫springvc

1.先建立maven的web專案,結構如下 2.在web.xml新增如下配置 <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>com.mayik

Android進階3:Activity原始碼分析2) —— Activity啟動和銷燬流程(8.0)

上篇文章講述了app從啟動建立Activity呼叫onCreate,onStart, onResume方法,這篇文章講述一下Activity啟動的另一個切入點:startActivity方法,啟動Activity。 通過上一篇文章,我們總結一下: 1:A

shiro原始碼分析之自定義註解RequiredPermission(可代替RequiresPermissions)

1.現象 shiro使用RequiresPermissions等註解必須新增如下切面,否則不生效 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(Security

Shiro原始碼分析(1)

簡介 SecurityManager:安全管理器,Shiro最核心元件。Shiro通過SecurityManager來管理內部元件例項,並通過它來提供安全管理的各種服務。 Authenticator:認證器,認證AuthenticationToken是否有

《springsecurity原始碼分析2.springsecurity原始碼分析之認證流程

Spring Security核心就是一系列的過濾器鏈,當一個請求來的時候,首先要通過過濾器鏈的校驗,校驗通過之後才會訪問使用者各種資訊。  SecurityContextPersistenceFilter 當一個請求來的時候,它會將session中的值傳入到該執

原始碼分析之String原始碼分析

    前面已經分析過String原始碼為什麼是不可變的,同時通過我們常用的String的相關的類StringBuffer和StringBuilder,我們可以發現String類中欄位名被定義為了final型別,這樣的話將只能被賦值一次。接下來,繼續看String原始碼實現的

原始碼分析之String原始碼分析

    上期已經討論了比較器的相關的問題。同時也提出問題明明父類已經實現了某個介面,子類為什麼還是要實現同樣的介面呢。這究竟有什麼意義呢。通過使用度孃的多次探究之後最終我還是沒有發現說它有什麼特別的含義。我認為的最大的含義就是告訴使用者這個類實現了這樣的一個介面。方便使用者進

tensorflowV1.11-原始碼分析2

通過前面的run_shell函式,執行python指令碼,並返回python庫的路徑。 def get_python_path(environ_cp, python_bin_path): """Get the python site package paths.""" python_paths = [

Django rest framework原始碼分析2)----許可權

目錄 新增許可權 (1)API/utils資料夾下新建premission.py檔案,程式碼如下: message是當沒有許可權時,提示的資訊 # utils/permission.py class SVIPPremission(object): message =