spring security源码debug [0]

spring security的典型用法

配置

下面是spring-security.xml的配置

<beans>
<http pattern="/js/**" security="none"/>
<http pattern="/logout.jsp" security="none"/>
<http auto-config="true" use-expressions="true"
    authentication-manager-ref="authenticationManager">
    <headers>
            <frame-options policy="SAMEORIGIN" />
    </headers>
    <csrf disabled="true" />
    <form-login login-page="/login.jsp"
            authentication-failure-url="/login.jsp?error=1"
            always-use-default-target="true"
            default-target-url="/"
            login-processing-url="/j_spring_security_check"
            authentication-success-handler-ref="simpleAuthenticationSuccessHandler"/>
    <logout logout-url="/j_acegi_logout" logout-success-url="/logout.jsp" delete-cookies="JSESSIONID"/>
    <intercept-url pattern="/**" access="isAuthenticated()" />
    <access-denied-handler error-page="/deny.jsp" />
</http>


<authentication-manager alias="authenticationManager">
        <authentication-provider ref="simpleAuthenticationProvider" />
</authentication-manager>
</beans>

在web.xml中添加spring的DelegatingFilterProxy

        <!-- Enables Spring Security
        -->
        <filter>
                <filter-name>springSecurityFilterChain</filter-name>
                <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
                <init-param>
                        <param-name>contextAttribute</param-name>
                        <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.controlplane</param-value>
                </init-param>
        </filter>
        <filter-mapping>
                <filter-name>springSecurityFilterChain</filter-name>
                <url-pattern>/*</url-pattern>
        </filter-mapping>

其中contextAttribute配置的spring的webapplicationcontext的key name,注意org.springframework.web.servlet.FrameworkServlet.CONTEXT.controlplane最后一个单词controlplane是当前webapp的context的name。

效果

简单描述一下以上配置的效果,理解后可以举一反三。每一个都对应一个bean,类型就是DelegatingFilterChain,他们与web.xml中的其他filter同一个层次。当请求一个url时,按注册的顺序匹配各个DelegatingFilterChain匹配的url,如果匹配,那么当前bean根据自己的配置组织一串filter,叫additionalFilter。如security="none"则不用过滤,继续调用web.xml中定义的其他filter。

对于上文配置中最后一个,挑选一些重要的点,

  • 属性authentication-manager-ref,指定用来做authentication的bean,bean会继续委托providers来做authentication。
  • 子标签. SAMEORIGIN表示该url只能被同源的页面的iframe引用
  • 其他待定。。

背后的原理是什么

authentication的过程是什么

假设用户请求一个需要authentication的url,经过一系列的filter,如图:

其中UsernamePasswordAuthenticationFilter,是用户名密码方式authentication,如果url不匹配,则跳过当前filter,否则尝试鉴权,从request中准备好username和password,如果sessionId存在也一起放到token中。调用AuthencationManager进行授权。以ProviderAuthenticationManager为例,遍历所有Provider,判断Provider是否支持当前token类型,如果支持那么使用provider鉴权,可能返回鉴权后的token,null或者抛异常。如果失败还委托给parent AuthenticationManager。调用层次就是UsernamePasswordAuthenticationFilter => AuthencationManager => Provider
如果鉴权成功,调用SessionAuthencationStrategy.onAuthenction,实际就是处理Session,如果不存在就创建,存在则更新。??后续呢
如果鉴权失败,那么就filter返回,不再继续后续filter,本例中UsernamePasswordAuthencationFilter只过滤/j_spring_securty_check这个url。如果请求该url,就是单纯为了鉴权,并不是为了其他的资源。
大多数资源都因为不匹配url,而跳过UsernamePasswordAuthencationFIlter。

DefautltLoginPageGeneratingFilter,定义了loginPageUrl,failureUrl,logoutSuccessUrl,如果匹配,那么生成一个页面作为response返回客户端。否则继续下一个filter。

BasicAuthenticationFilter,只过滤Header中指定Authorization是Basic的request。如果该username还没有鉴权,则尝试AuthenticationManager鉴权,authentication-manager-ref注册的bean会作为BasicAuthenticationFilter使用AuthenticationManager的parent被使用。如果成功,把token保存到securitycontext(LocalThread的),springsecurtiycontext在后续的sessionmanagerfilter最终会被保存到session对象中。如果鉴权失败则根据配置决定继续filter或者返回失败页面。如果鉴权成功继续filter,此时得到的成果只是把AuthenticationToken对象保存到了SpringSecurityContext对象。

下一个RequestCacheAwareFilter,待定。用cached request替换request传递到下一个filter

SecurityContextHolderAwareFilter。wrap request,传递到下一个filter

AnonymousAuthenticationFilter。如果SpringSecurityContext中没有Authentication,那么创建一个anonymousAuthentication并set到SpringSecurityContext,主要是为了避免null,使得后续处理保持一致。

SessionManagementFilter。如果当前req的session不存在对应的context,(未提供sessionid或者tomcat的ManagerBase中不能找到sessionid指定的session对象),那么就保存到session,如果session不存在,那么就创建一个session对象,保存到SPRING_SECURITY_CONTEXT。???一直来新的匿名请求不就一直有session吗?哦,没事web.xml中配置了timeout时间。

ExceptionTranslationFilter。先继续下一个filter,如果捕获到AuthenticationException就cache request和response,呼应之前的RequestCacheAwareFilter,使用定义好的AuthenticationEntryPoint处理当前request。如果AccessDeniedException那么调用对应的accessDeniedHandler。

FilterSecurityInterceptor。根据配置的,如果url需要拦截调用,获取Authentication对象,使用AccessDecisionManager决定是否通过。策略可以是一票通过,一票否决,多数票通过三种策略。Authentication中包含丰富的user信息,可以灵活decide,主要是role层次的授权,业务相关的授权不适合在这里完成。在中通过SPEL表达式配置进行决策投票。如果授权决策通过,那么继续filter,否则抛出异常AccessDeniedException。在执行filter和service结束后,还会调用AfterInvocationManager,更多是关注response的结果进行拦截,比较少用。

Tomcat的session

纵观整个过程,默认tomcat不会为每一个request创建session,而是由webapp自行决定,spring-security是在Authentication成功之后保存SpringSecurityContext的时候,创建session对象。并且如果是AnonymousAuthentication在SessionManagementFilter中也不会saveSpringSecurityContext到session,所以也不会为之创建session对象。

tomcat的Session对象存储有StandardManager和PersistentManager,前者是内存保存,后者还提供文件保存和jdbc保存。在request.getSession的时候可以选择不存在session时是否创建。

request的整个经历

多个filter的完成。
filter(web.xml)=> DelegatingFilterProxy => filter(web.xml)=> service
filter(web.xml)<= DelegatingFilterProxy <= filter(web.xml)<= service
这是一个调用的过程,是嵌套的,而不是链式地一个完成之后就丢掉不再回头。

posted on 2019-12-06 09:28  还好可以改名字  阅读(426)  评论(0编辑  收藏  举报