Spring-Security 运行流程

Spring-Security 运行流程

web.xml配置

首先在web.xml中配置如上图所示的过滤器,但这其实不是spring-security包下的类,而是spring-web下面的一个类,从名字上可以看出,这个类是一个过滤器的代理,根据我查阅资料,这样做的原因是:spring-security的过滤器链中的每一个都必须被注入来自 Spring 应用程序上下文的其他 Bean. 但 Servlet 规范并没有使得 Servlet 过滤器上的依赖注入容易进行. DeletegatingFilterProxy 通过充当 Spring 应用程序中被配置为 Bean 的实际过滤器的 “挂名人物” 来解决这个问题.真正起作用的 Filter 是 Spring 上下文中的那些 Filter Bean. web.xml 中的代理依次调用这些Bean, 实现对Web资源的保护。可以参考我的另一篇博客:http://blog.csdn.net/u012156496/article/details/53485273

spring-security过滤器链

通过调试发现,最终DelegatingFilterProxy中生成一个FilterChainProxy对象,且在这个对象中,包含了security的过滤器链,过滤器链有三组,第一组是针对登录页面的,第二组是登出页面,第三组是对任何请求都过滤,权限的控制也是在这组过滤器链中实现的。我的理解是,掌握第三组过滤器链的作用,就基本对spring-security有一个基本的使用上掌握了。

下面来详细看下这个过滤器链:
权限控制过滤器链

第一个过滤器:SecurityContextPersistenceFilter
主要作用是SecurityContext的组装,供后续的过滤器链来使用,如果用户已经登录,再次访问其他资源时,会根据sessionId在session中将以前保存的SecurityContext取出,SecurityContext保存有用户的登录信息,那么就不需要用户再次登录了,只需要验证该用户是否有权限访问该资源即可。
在SecurityContextPersistenceFilter的doFilter方法中:
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
上面一行代码,则是取出SecurityContext,如果用户第一次访问,还没有,则返回null。

第二个过滤器:LogoutFilter
这个是spring-security的登出功能,当我们在spring-security配置文件的security:http节点,配置auto-config为true时(如下图),那么spring-security就会为我们自动加载logout过滤器。之后就可以使用spring-security的logout功能了,可以参考spring security logout(spring security登出示例),我翻译的一篇外文,是logout功能的使用范例。

这里写图片描述

第三个过滤器:UsernamePasswordAuthenticationFilter
见名知意,这个过滤器是用来验证用户的登录凭证的,可以通过这个过滤器来实现从数据库取出数据,来验证用户登录凭证是否正确。在我学习的过程中,发现刚开始应该只需要初步的了解这个过滤器的执行流程,就能基本的解决一些需求了.什么意思呢,就是我发现最后,其实这个过滤器,就是取出form表单中用户提交的登录信息,来和我们配置在配置文件中的用户信息,或者是通过数据库中取出来的用户信息来匹配,匹配成功,则通过。刚开始我学习的很迷茫,就是把问题搞复杂了,其实看清本质就好。不废话,直接说如何从数据库取用户信息。

这里写图片描述

上面这段springSecurity的配置文件很重要,先说有关从数据中库取用户登录凭证的。

既然是从数据库取用户信息,那么取数据的实现代码,肯定是要我们自己写的这里写图片描述
上面就是这个实现了org.springframework.security.core.userdetails.UserDetailsService接口的实现类,就是通过实现这个类,我们根据登录用户的名称,取出用户信息,在封装到springSecurity要的User对象中,返回给springSecurity框架来使用,就可以了。再配置文件中,我们通过springSecurity的配置文件来注入我们的这个实现类。这样就实现了从数据库中,去取用户的登录信息。

第四个过滤器:BasicAuthenticationFilter
org.springframework.security.web.authentication.www.BasicAuthenticationFilter,这个过滤器是当开启Basic验证方式的时候,才有用,关于basic验证方式,就是一种将用户的验证信息放在http请求头的中用户验证方式。如果请求头不是以Basic开头,则这个过滤器是不会起作用的 这里写图片描述

第五个过滤器:RequestCacheAwareFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter这个filter的用途官方解释是:
用于用户登录成功后,重新恢复因为登录被打断的请求
这个解释也有几点需要说明
被打断的请求:简单点说就是出现了AuthenticationException、AccessDeniedException两类异常
重新恢复:既然能够恢复,那肯定请求信息被保存到cache中了

第六个过滤器:SecurityContextHolderAwareRequestFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
从类名称可以猜出这个过滤器主要是包装请求对象request的,看源码

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    chain.doFilter(new SecurityContextHolderAwareRequestWrapper((HttpServletRequest) req, rolePrefix), res);
}

   SecurityContextHolderAwareRequestWrapper类对request包装的目的主要是实现servlet api的一些接口方法isUserInRole、getRemoteUser。
这个过滤器看起来很简单。目的仅仅是实现java ee中servlet api一些接口方法。
一些应用中直接使用getRemoteUser方法、isUserInRole方法,在使用spring security时其实就是通过这个过滤器来实现的。

第七个过滤器:AnonymousAuthenticationFilter
对应的类路径为:org.springframework.security.web.authentication.AnonymousAuthenticationFilter.
AnonymousAuthenticationFilter过滤器是在UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、RememberMeAuthenticationFilter这些过滤器后面的,所以如果这三个过滤器都没有认证成功,则为当前的SecurityContext中添加一个经过匿名认证的token,但是通过servlet的getRemoteUser等方法是获取不到登录账号的。因为SecurityContextHolderAwareRequestFilter过滤器在AnonymousAuthenticationFilter前面。

//省略了日志部分
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    //applyAnonymousForThisRequest永远返回ture
    if (applyAnonymousForThisRequest((HttpServletRequest) req)) {
            //如果当前SecurityContext中没有认证实体
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                //产生一个匿名认证实体,并保存到SecurityContext中
                SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));
            } else {}
        }

        chain.doFilter(req, res);
    }

    protected Authentication createAuthentication(HttpServletRequest request) {
        //产生匿名认证token,注意这里的key、userAttribute是通过解析标签注入的
        AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key, userAttribute.getPassword(),
                userAttribute.getAuthorities());
        auth.setDetails(authenticationDetailsSource.buildDetails(request));

        return auth;
    }

anonymous标签配置为。

<anonymous granted-authority="ROLE_ANONYMOUS" enabled="true" username="test"/>

这里username属性容易混淆,username默认为anonymousUser,实际上是注入到UserAttribute的password变量中的。
granted-authority属性注入到UserAttribute的authorities授权列表。

第十个过滤器:MyFilterSecurityInterceptor
这个过滤器是自定义来实现权限控制逻辑的,通过spring-security配置文件来注入到spring-security的过滤器链中的,这个过滤器需要继承org.springframework.security.access.intercept.AbstractSecurityInterceptor。
配置文件如下:

<security:http auto-config="true" access-denied-page="/403.jsp" >
        <security:form-login login-page="/login.jsp"/>
        <!-- 将自定义的实现了权限控制逻辑的过滤器加入到spring-security的过滤器链中 -->
        <security:custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter"/>
    </security:http>

    <!-- 注入MyFilterSecurityInterceptor需要的相关bean -->
    <bean id="myFilter" class="com.wr1ttenyu.security.MyFilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
        <property name="securityMetadataSource" ref="securityMetadataSource" />
    </bean>

    <!-- 将实现了用户信息获取的bean注入到authenticationManager中 -->
    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider user-service-ref="myUserDetailService">
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- 自定义的获取用户信息的实现类 -->
    <bean id="myUserDetailService" class="com.wr1ttenyu.security.MyUserDetailService" />

    <!-- 这里myAccessDecisionManagerBean是用来注入到AbstractSecurityInterceptor中accessDecisionManager属性的,
        用来实现权限控制的具体逻辑 -->
    <bean id="myAccessDecisionManagerBean" class="com.wr1ttenyu.security.MyAccessDecisionManagerBean" />

    <!-- 定义实现了权限控制逻辑的过滤器bean -->
    <bean id="securityMetadataSource" class="com.wr1ttenyu.security.MyInvocationSecurityMetadataSource" />

配置好之后,该过滤器就会被加入到过滤器链中,具体实现权限验证的代码如下:

public void invoke(FilterInvocation fi) throws IOException, ServletException {
        // 通过调用父类AbstractSecurityInterceptor的beforeInvocation方法来实现
        // 值得注意的是,在beforeInvocation实现权限控制的逻辑中,如果请求的资源路径在securityMetadataSource中没有的话,
        // 那么就会默认为该资源无权限要求,只要请求就会通过。
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

以上内容主要参考:http://www.codeweblog.com站内关于spring-security源码分析的文章
未完待续。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值