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。
效果
简单描述一下以上配置的效果,理解后可以举一反三。每一个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。根据配置的
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
这是一个调用的过程,是嵌套的,而不是链式地一个完成之后就丢掉不再回头。