這篇寫(xiě)得非常不錯(cuò),很細(xì) 原文地址:http://blog.csdn.net/jacky_zuo/article/details/7000402 Shiro的過(guò)濾器的配置是結(jié)合使用Spring的DelegatingFilterProxy與FactoryBean2種技術(shù)來(lái)完成自身過(guò)濾器的植入的,所以理解Shiro的過(guò)濾器首先要理解這2者的使用。
1. DelegatingFilterProxy Spring提供的一個(gè)簡(jiǎn)便的過(guò)濾器的處理方案,它將具體的操作交給內(nèi)部的Filter對(duì)象delegate去處理,而這個(gè)delegate對(duì)象通過(guò)Spring IOC容器獲取,這里采用的是Spring的FactoryBean的方式獲取這個(gè)對(duì)象。 DelegatingFilterProxy的配置如下
- <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>
雖然只配置了這一個(gè)filter,但是它并做任何實(shí)際的工作,而是把工作交由Spring中容器為bean的名字shiroFilter的類(lèi),即ShiroFilterFactoryBean;
2. ShiroFilterFactoryBean 配置如下 - <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- ..
- </bean>
由于它是個(gè)FactroyBean,所以上面的delegate真正的對(duì)象是通過(guò)它的getObject()獲取的。 這里是FactoryBean接口獲取實(shí)例的標(biāo)準(zhǔn)方法
- public Object getObject() throws Exception {
- if (instance == null) {
- instance = createInstance();
- }
- return instance;
- }
這里是真正創(chuàng)建對(duì)象的方法
- 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);
- }
所以真正完成實(shí)際工作的過(guò)濾器是SpringShiroFilter,這個(gè)對(duì)象才是真正的delegate。
3. SpringShiroFilter: ShiroFilterFactoryBean的內(nèi)部類(lèi),繼承AbstractShiroFilter
- private static final class SpringShiroFilter extends AbstractShiroFilter {
-
- protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
- super();
- if (webSecurityManager == null) {
- throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
- }
- setSecurityManager(webSecurityManager);
- if (resolver != null) {
- setFilterChainResolver(resolver);
- }
- }
- }
4. OncePerRequestFilter : AbstractShiroFilter的父類(lèi)關(guān)鍵方法 - protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
- throws ServletException, IOException;
這個(gè)方法有過(guò)濾器中調(diào)用:
- public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
- if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(request)) {
- log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());
- // Proceed without invoking this filter...
- 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);
- }
- }
- }
doFilterInternal這個(gè)方法有2處實(shí)現(xiàn),1是AbstractShiroFilter的實(shí)現(xiàn),2是AdviceFilter的實(shí)現(xiàn)。通過(guò)查看shiro的內(nèi)定義的Filter繼承結(jié)構(gòu)可以看出,除了SpringShiroFilter這個(gè)內(nèi)部類(lèi)是繼承前者,其他所有的用到的Filter都是繼承后者。SpringShiroFilter是每次請(qǐng)求的第一個(gè)真正處理實(shí)際工作的Filter(主要是創(chuàng)建一個(gè)Subject并綁定相關(guān)數(shù)據(jù))。5. AbstractShiroFilter:OncePerRequestFilter的第一個(gè)子類(lèi) - 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);
- }
- }
這段代碼表示每次經(jīng)過(guò)AbstractShiroFilter的doFilterInternal方法(具體的類(lèi)也就是上面的內(nèi)部類(lèi)SpringShiroFilter)都會(huì)創(chuàng)建一個(gè)新的Subject,具體分析里面的代碼可以發(fā)現(xiàn),這個(gè)Subject的數(shù)據(jù)會(huì)從SubjectContext或Session中獲取過(guò)來(lái)。這意味著每次經(jīng)過(guò)Shiro過(guò)濾器的HTTP請(qǐng)求,都會(huì)創(chuàng)建一次新的Subject.Suject里面的數(shù)據(jù),主要是從SubjectContext中獲取,但是獲取方式不一樣,如SecurityManager總是從SubjectContext中直接獲取,而其他數(shù)據(jù)則主要從Session中獲取。只有在登錄操作的時(shí)候數(shù)據(jù)會(huì)都從SubjectContext上下文中獲取。因?yàn)榈卿洺晒筮€會(huì)有一個(gè)綁定操作,它會(huì)把當(dāng)前用戶(hù)的相關(guān)信息寫(xiě)入Session中去。 DefaultSecurityManager代碼如下:
- protected void bind(Subject subject) {
- // TODO consider refactoring to use Subject.Binder.
- // This implementation was copied from SessionSubjectBinder that was removed
- PrincipalCollection principals = subject.getPrincipals();
- if (principals != null && !principals.isEmpty()) {
- Session session = subject.getSession();
- bindPrincipalsToSession(principals, session);
- } else {
- Session session = subject.getSession(false);
- if (session != null) {
- session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
- }
- }
-
- if (subject.isAuthenticated()) {
- Session session = subject.getSession();
- session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, subject.isAuthenticated());
- } else {
- Session session = subject.getSession(false);
- if (session != null) {
- session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
- }
- }
- }
- private void bindPrincipalsToSession(PrincipalCollection principals, Session session) throws IllegalArgumentException {
- if (session == null) {
- throw new IllegalArgumentException("Session argument cannot be null.");
- }
- if (CollectionUtils.isEmpty(principals)) {
- throw new IllegalArgumentException("Principals cannot be null or empty.");
- }
- session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, principals);
- }
其他登錄相關(guān)的信息綁定到SubjectContext的操作代碼如下,每個(gè)set方法的調(diào)用都將數(shù)據(jù)保存到SubjectContext: - protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {
- SubjectContext context = createSubjectContext();
- context.setAuthenticated(true);
- context.setAuthenticationToken(token);
- context.setAuthenticationInfo(info);
- if (existing != null) {
- context.setSubject(existing);
- }
- return createSubject(context);
- }
6. AdviceFilter:OncePerRequestFilter的第二個(gè)子類(lèi) 它是全部的驗(yàn)證與授權(quán)Filter的父類(lèi),其doFilterInternal方法承擔(dān)此類(lèi)過(guò)濾器的核心邏輯。 - 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);
- }
- }
從上面的代碼可以看出,其核心的邏輯是3個(gè)部分: preHandle, executeChain,postHandle。后2者都只有該類(lèi)中有唯一的實(shí)現(xiàn),子類(lèi)并不覆蓋,而preHandle則由一個(gè)子類(lèi)PathMatchingFilter中覆蓋,代碼如下:- public boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
-
- if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
- if (log.isTraceEnabled()) {
- log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");
- }
- return true;
- }
-
- for (String path : this.appliedPaths.keySet()) {
-
-
- if (pathsMatch(path, request)) {
- if (log.isTraceEnabled()) {
- log.trace("Current requestURI matches pattern [" + path + "]. Performing onPreHandle check...");
- }
- Object config = this.appliedPaths.get(path);
- return onPreHandle(request, response, config);
- }
- }
-
-
- return true;
- }
這個(gè)方法根據(jù)用戶(hù)請(qǐng)求的地址是否與該Filter配置的地址匹配來(lái)決定是否調(diào)用內(nèi)部的onPreHandler方法。從shiroFilter中的屬性filterChainDefinitions配置中可以看出,shiro默認(rèn)的那些過(guò)濾器如user,roles,perms等等都可以統(tǒng)一使用這種方式,對(duì)于內(nèi)部的處理則分別由各個(gè)Filter的onPreHandler(其實(shí)是由內(nèi)部的isAccessAllowed和onAccessDenied方法)來(lái)決定了。 舉2個(gè)例子 第一個(gè)是AuthenticationFilter的isAccessAllowed方法,它只檢測(cè)用戶(hù)是否通過(guò)驗(yàn)證 - protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
- Subject subject = getSubject(request, response);
- return subject.isAuthenticated();
- }
第二個(gè)是RolesAuthorizationFilter的isAccessAllowed方法,它檢測(cè)用戶(hù)的角色是否滿(mǎn)足
- public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
-
- Subject subject = getSubject(request, response);
- String[] rolesArray = (String[]) mappedValue;
-
- if (rolesArray == null || rolesArray.length == 0) {
-
- return true;
- }
-
- Set<String> roles = CollectionUtils.asSet(rolesArray);
- return subject.hasAllRoles(roles);
- }
|