spring整合shiro

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
id="WebApp_ID" version="3.1">
  <!-- shiro过虑器,DelegatingFilterProx会从spring容器中找shiroFilter -->
    <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>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
View Code

 

spring_shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">  

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/"/>
        <property name="filters">
            <map>
                <!--<entry key="authc" value-ref="authorizationFilter"/>-->
                <entry key="authc" value-ref="authenticationFilter"/>
            </map>
        </property>
        <!-- anon    org.apache.shiro.web.filter.authc.AnonymousFilter
        authc    org.apache.shiro.web.filter.authc.FormAuthenticationFilter
        authcBasic    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
        logout    org.apache.shiro.web.filter.authc.LogoutFilter
        noSessionCreation    org.apache.shiro.web.filter.session.NoSessionCreationFilter
        perms    org.apache.shiro.web.filter.authz.PermissionAuthorizationFilter
        port    org.apache.shiro.web.filter.authz.PortFilter
        rest    org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
        roles    org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
        ssl    org.apache.shiro.web.filter.authz.SslFilter
        user    org.apache.shiro.web.filter.authz.UserFilter -->
        <property name="filterChainDefinitions">
            <value>
                /app/**=anon
                /css/**=anon
                /static/**=anon
                /upload/**=anon
                /login_manager=anon
                /isAffirm=anon
                /getAdviceNoteForId=anon
                /**=authc
            </value>
        </property>
    </bean>
    <!-- securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realms">
            <list>
                <ref bean="shiroRealm"/>
            </list>
        </property>
        <!-- 注入缓存管理器 -->
        <property name="cacheManager" ref="cacheManager"/>
        <!-- 注入session管理器 -->
        <property name="sessionManager" ref="sessionManager"/>
        <!-- 记住我 -->
    </bean>
    <bean id="shiroRealm" class="org.magicabc.pc.shiro.ShiroRealm">
        <!-- <property name="credentialsMatcher" ref="credentialsMatcher"/> -->
    </bean>

    <bean id="redisSessionDAO" class="org.magicabc.pc.shiro.RedisSessionDao"/>
    <bean id="cacheManager" class="org.magicabc.pc.shiro.RedisCacheManager">
        <property name="redisTemplate" ref="redisTemplate"/>
    </bean>

    <bean id="credentialsMatcher"
          class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
    </bean>

    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="globalSessionTimeout" value="1800000"/>
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        <property name="sessionDAO" ref="redisSessionDAO"/>
        <!-- <property name="sessionListeners"> 
            <list> 
                <bean class="org.magicabc.pc.shiro.ShiroSessionListener"/> 
            </list>
        </property> -->
    </bean>

    <!--<bean id="authorizationFilter" class="com.hunt.system.security.shiro.ShiroAuthorizationFilter"/>-->
    <bean id="authenticationFilter" class="org.magicabc.pc.shiro.ShiroAuthenticationFilter"/>

    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor">
          <property name="proxyTargetClass" value="true"></property>
    </bean>
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

</beans>
View Code

 

相关类:

 

关于这份配置文件和相关类,需要系统的说明一下,也算是对spring集成shiro有一个总结,关于什么是shiro的简介就不说了 ,直接捡关键要点

1.为什么使用shiro

在之前的项目中,如果对用户进行登陆验证,需要自己把用户登陆信息保存到相应的session中,如果不同角色的用户登陆可以操作不同的功能时,需要自己每一次都从数据库中拿数据然后和session中的数据进行比对。

 当角色满足功能表对应的条件时,才可以获取到相应的功能列表。当一个功能对应多个角色时,这样的操作时几位不方便的。这个东西说的专业一点,就叫权限操作。shiro,就是为了解决这个问题才被创造的。

2.shiro为什么就能管理权限呢

这个就需要从shiro的内部结构开始说起了,

首先看一幅图,这图是《跟我学shiro》中的shiro结构:

来说说这幅图吧:

首先是subjuect,主体,任何访问的对象都是一个主体。

然后是SecurityManager,这个东西是个大管家,所以主体来的请求,都归它管。

Authenticator,认证用的,什么叫认证,就是看看是不是这个角色。

Authorizer,授权用的,什么叫授权,一把钥匙开一把锁,能打开这把锁就是授权。

Realm,这东西叫域,说的通俗一点,就是个数据源。认证,授权所需要的数据,都在这里边放着呢。这里边的数据哪里来的?从数据库或者配置文件,都行。

SessionManager,会话管理用的。什么是会话?就是session.集中管理session.

SessionDao,这是个把session保存到数据库中的东西,值得一提,这东西可以使用缓存。

CacheManager,上边刚说可以使用缓存,这个东西就来了,这是管理缓存的。

Crytography,解密加密必备趁手工具。

结构说了一大堆,但是shiro怎么就能管理权限了?

看图:

3.shiro的过滤器

在项目中使用shiro,核心是使用shiro的过滤器。

图片来自《跟我学shiro》。上图就是shiro的过滤器类别分布。

在项目中,和shiro相关的类都在这些jar包中

Filter:是一个接口,在javax.servlet包下,其中只有三个方法:

public void init(FilterConfig filterConfig) throws ServletException;

public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException;
public void destroy();
View Code

 

AbstractFilter,NameableFilter,OncePerRequestFilter,AdviceFilter,AbstractShiroFilter和shiroFilter这几个抽象类,全部都在一个包下

AbstractFilter实现了Filter这个接口,它是一个抽象类,内部定义了一个过滤器配置文件属性,并添加了get/set方法。然后只对Filter的destory和init方法进行了重写

顺便说一句,@Override这个注解写不写都可以,如果实现类和接口中方法构成重写关系,不写也没事,JVM会对这个进行处理,但还是写上为好,如果没有进行方法重写,那么在写上去以后编译器自动报错)

NameableFilter继承自AbstractFilter这个抽象类,这个类就是给过滤器起名的.名字就是配置文件里写的,如果配置文件有,就从配置文件这里获取

 protected String getName() {
        if (this.name == null) {
            FilterConfig config = getFilterConfig();
            if (config != null) {
                this.name = config.getFilterName();
            }
        }

        return this.name;
    }
View Code

 

OncePerRequestFilter这个抽象类继承自NameableFilter,这个过滤器的作用是保证一次请求只走一次过滤器链.

Filter base class that guarantees to be just executed once per request,
on any servlet container. It provides a {@link #doFilterInternal}
method with HttpServletRequest and HttpServletResponse arguments.
View Code

 在这个类中,它自己重写了doFilter方法,这个就是从Filter这个接口一路下来神秘消失的DoFilter,在这里它被重新定义赋予了新的功能,来看代码

   /**
     * This {@code doFilter} implementation stores a request attribute for
     * "already filtered", proceeding without filtering again if the
     * attribute is already there.
     *
     * @see #getAlreadyFilteredAttributeName
     * @see #shouldNotFilter
     * @see #doFilterInternal
     */
    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);
            }
        }
    }
View Code

先看说明:这个Filter的实现类存储了已经通过滤过的请求中的属性,再次访问的时候,如果属性已经在这里存在了,就不会通过这个过滤器了

可以说这个就是OncePerRequestFilter的核心.

方法内部,第一句String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();需要获取已经通过过滤器的访问属性名,这是另一个方法

protected String getAlreadyFilteredAttributeName() {
        String name = getName();
        if (name == null) {
            name = getClass().getName();
        }
        return name + ALREADY_FILTERED_SUFFIX;
    }
View Code

getName()就是NameableFilter的getName(),也就是获取配置文件中过滤器的名字.如果从配置文件中获取不到,就通过反射直接从源文件中获取这个过滤器的名字.

在返回时,在这个名字加上后缀+ ALREADY_FILTERED_SUFFIX,这个 ALREADY_FILTERED_SUFFIX实际上是".FILTERED",这个单词代表已经通过的.

好了现在已经获取到已经通过过滤器的访问属性名了,接下来看后边的代码,开始进行比较了,如果请求中包含这个属性,那么久直接去执行过滤器链中后边的过滤器,

然后else if(!isEnabled(request, response) ||shouldNotFilter(request)),这两个条件是什么意思?

先来看第一个,isEnabled(request, response),看看这个请求是否被允许,默认返回的是true,表示当前过滤器要执行,request和response直接进入过滤器链的下一个.

第二个是个过期方法,返回的是个false;

所以这个条件合起来的意思就是在说如果这个过滤器不执行,就直接进入过滤器链的下一个过滤器.

官方注释: log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());已经执行过了,不执行这个过滤器.

剩下的就让没有访问过的走这个过滤器,执行doFilterInternal,最后,使用finally修饰,在请求中一定要移除已经通过过滤器的访问属性名.

AbstractShiroFilter是有一个抽象类,这个类的干了非常多的事,而且是很有必要的。ShiroFilte这个类是Shiro的入口,很多关键的方法都在AbstractShiroFilter中实现。

这个抽象类下有三个重要的属性:securityManager,filterChainResolver,staticSecurityManagerEnabled,并分别添加了getset方法

WebSecurityManager接口继承自SecurityManager,为这个过滤器引入SecurityManager;

FilterChainResolver接口用来判断哪一个过滤器是和请求或相应相匹配的.

staticSecurityManagerEnabled是一个boolean;是否为静态内存创建安全管理器,默认是false;

 然后看几个重要的方法:

1:init():初始化过滤器,没有任何实现,等待ShiroFIlter继承

 public void init() throws Exception {
    }
View Code

2:createSubject(ServletRequest request, ServletResponse response):创建网络访问的主体

protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
        return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
    }
View Code

3:doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain):执行内部过滤器

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);
        }
    }
View Code

如果还有印象,这个方法就是对OncePerRequestFilter中doFilterInternal方法的重写。这个方法 的核心是这两句:

创建subject,然后由subjuct执行,在匿名内部类中,调用call()方法,首先更新最后一次访问时间的Session,然后再执行过滤器链。

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

这个里边调用了自己的getExecutionChain(request, response, origChain),来看看这个方法说了什么

 protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
        FilterChain chain = origChain;

        FilterChainResolver resolver = getFilterChainResolver();
        if (resolver == null) {
            log.debug("No FilterChainResolver configured.  Returning original FilterChain.");
            return origChain;
        }

        FilterChain resolved = resolver.getChain(request, response, origChain);
        if (resolved != null) {
            log.trace("Resolved a configured FilterChain for the current request.");
            chain = resolved;
        } else {
            log.trace("No FilterChain configured for the current request.  Using the default.");
        }

        return chain;
    }
View Code

从过滤器链解析器获取解析到的结果,然后得到过滤器链,将这个过滤器链返回。

ShiroFilter这个类前边已经说过了,它是FilterShiro的入口,它继承自AbstractShiroFilter,并且不是一个抽象类。在它里边只有一个方法,init()

public void init() throws Exception {
        WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext());

        setSecurityManager(env.getWebSecurityManager());

        FilterChainResolver resolver = env.getFilterChainResolver();
        if (resolver != null) {
            setFilterChainResolver(resolver);
        }
    }
View Code

这个init()方法是对它父类init()方法的重写,来看看它究竟干了什么事。

第一步:通过WebUtils这个工具类获取RequiredWebEnvironment,这些信息到保存在servletContext上下文对象中,所以可以直接获取。

得到一个WebEnvironment对象,这个对象是一个接口,继承自Environment这个接口

 

public interface WebEnvironment extends Environment {

    /**
     * Returns the web application's {@code FilterChainResolver} if one has been configured or {@code null} if one
     * is not available.
     *
     * @return the web application's {@code FilterChainResolver} if one has been configured or {@code null} if one
     *         is not available.
     */
    FilterChainResolver getFilterChainResolver();

    /**
     * Returns the {@code ServletContext} associated with this {@code WebEnvironment} instance.  A web application
     * typically only has a single {@code WebEnvironment} associated with its {@code ServletContext}.
     *
     * @return the {@code ServletContext} associated with this {@code WebEnvironment} instance.
     */
    ServletContext getServletContext();

    /**
     * Returns the web application's security manager instance.
     *
     * @return the web application's security manager instance.
     */
    WebSecurityManager getWebSecurityManager();
}
View Code

 

public interface Environment {

    /**
     * Returns the application's {@code SecurityManager} instance.
     *
     * @return the application's {@code SecurityManager} instance.
     */
    SecurityManager getSecurityManager();
}
View Code

从WebEnvironment和Environment这两个接口的方法可以知道,这俩其实就是获取SecurityManager,ServletContext、FilterChainResolver

 

第二步:从WebEnvironment对象中获取WebSecurityManager,并把它设置为SecurityManager。这个setSecurityManager的方法,是AbstractShiroFilter的,于是就有了SecurityManager

第三步:从WebEnvironment对象中获取FilterChainResolver,如果这个FilterChainResolver不是空,调用setFilterChainResolver方法,这个方法也是AbstractShiroFilter的,于是就有了FilterChainResolver

既然说到shiroFilter是入口了,它怎么就成了入口了呢?

让我重新看看web.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>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
View Code

 

配置文件中定义了filter的名字是shiroFilter,并且映射到了org.springframework.web.filter.DelegatingFilterProxy。

DelegatingFilterProxy是对servlet filter的代理,它可以直接把shiroFilter交由spring容器管理,那么是怎么做到的呢?来看源码吧。因为这个项目使用的spring版本是4.0.2,所以需要从github找到相对应的版本源码包导入。

这个类继承自GenericFilterBean,然后GenericFilterBean呢?

这个抽象类实现了Filter,所以在servlet初始化之前,是首先调用filter的init()方法,GenericFilterBean的主要作用就是把Filter的初始化参数自动的set到继承于它的的filter中去。

把spring容器中的filter和spring中的bean进行关联。

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");
        }
    }
View Code

所以,在spring_shiro中这样配置,它才可以被识别:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/"/>
        <property name="filters">
            <map>
                <!--<entry key="authc" value-ref="authorizationFilter"/>-->
                <entry key="authc" value-ref="authenticationFilter"/>
            </map>
        </property>
    </bean>
View Code

在这里,可以看到,shiFilter对应的class并不是我们自定义的filter,而是ShiroFilterFactoryBean

这是个工厂类,所以我们可以配置多个自定义的过滤器,组成过滤器链。值得一提的是,

ShiroFilter在继续Servlet容器的过滤器链执行之前,会先走shiro自己的过滤器体系。

 

好了,接下来我们要继续梳理我们的shiro过滤器了。紧接着ShiroFilter,接下来我们要看的事AdviceFilter这个类

AdviceFilter这个抽象类提供了对AOP风格的支持,这一赖于其中的几个方法:

protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        return true;
    }
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
    }
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
    }
View Code

---以下摘自《跟我学shiro》第八节。

preHandler:相当于AOP的前置增强,在过滤器执行前先进性执行,如果返回true,继续执行过滤器链,否则终端后续的过滤器链的执行直接返回

,进行预处理(如基于表单的身份验证和授权)。

postHandle:相当于AOP的后置返回增强,在过滤器链执行之后在进行执行,进行后处理(记录执行时间等操作)。

afterCompletion:相当于AOP中的后置最终增强,不论有没有异常都会执行,可以进行清理资源(比如解除Subject和线程的绑定)

PathMatchingFilter提供了基于Ant风格的请求路径匹配功能和拦截器参数解析的功能,如“roles[admin,user]”自动根据“,”分割解析到一个路径参数配置并绑定到相应的路径。

 

protected boolean pathsMatch(String path, ServletRequest request) {
        String requestURI = getPathWithinApplication(request);
        log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, requestURI);
        return pathsMatch(path, requestURI);
    }
protected boolean pathsMatch(String pattern, String path) {
        return pathMatcher.matches(pattern, path);
    }
View Code

 

PathMath该方法用于path与请求路径进行匹配的方法,如果匹配返回rue;

protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return true;
    }
View Code

OnPreHandle:默认返回true.在preHandle中,当pathsMatch匹配一个路径后,会调用opPreHandler方法并将路径绑定参数配置传给mappedValue;然后可以在这个方法中进行一些验证(如角色授权),如果验证失败可以返回false中断流程;默认返回true;也就是说子类可以只实现onPreHandle即可,无须实现preHandle。如果没有path与请求路径匹配,默认是通过的(即preHandle返回true)

    protected 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 the path does match, then pass on to the subclass implementation for specific checks
            //(first match 'wins'):
            if (pathsMatch(path, request)) {
                log.trace("Current requestURI matches pattern '{}'.  Determining filter chain execution...", path);
                Object config = this.appliedPaths.get(path);
                return isFilterChainContinued(request, response, path, config);
            }
        }

        //no path matched, allow the request to go through:
        return true;
    }
View Code

preHandle:从路径配置中获取的对象appliedPaths(Map),如果不是空,就依次获取里边的路径.如果能和path匹配上,就放行.

AccessControlFilter提供了访问控制的基础功能;比如是否允许访问/当访问拒绝时如何处理等.

 

abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;  
boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;  
abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception; 
View Code

 

isAccessAllowed:表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;

onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。

onPreHandle会自动调用这两个方法决定是否继续处理:

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

另外AccessControlFilter还提供了如下方法用于处理如登录成功后/重定向到上一个请求:

void setLoginUrl(String loginUrl) //身份验证时使用,默认/login.jsp  
String getLoginUrl()  
Subject getSubject(ServletRequest request, ServletResponse response) //获取Subject实例  
boolean isLoginRequest(ServletRequest request, ServletResponse response)//当前请求是否是登录请求  
void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException //将当前请求保存起来并重定向到登录页面  
void saveRequest(ServletRequest request) //将请求保存起来,如登录成功后再重定向回该请求  
void redirectToLogin(ServletRequest request, ServletResponse response) //重定向到登录页面  
View Code

 

posted @ 2017-12-19 23:13  一介書生  阅读(292)  评论(0)    收藏  举报