上述介绍的就是 Spring Security 的认证过程。在认证成功后,用户就能够继续操做去访问其它受保护的资源了,可是在访问的时候将会使用保存在 SecurityContext 中的 Authentication 对象进行相关的权限鉴定。session
若是用户直接访问登陆页面,那么认证过程跟上节描述的基本一致,只是在认证完成后将跳转到指定的成功页面,默认是应用的根路径。若是用户直接访问一个受保护的资源,那么认证过程将以下:线程
在上述步骤中将有不少不一样的类参与,但其中主要的参与者是 ExceptionTranslationFilter。3d
ExceptionTranslationFilter 是用来处理来自 AbstractSecurityInterceptor 抛出的 AuthenticationException 和 AccessDeniedException 的。AbstractSecurityInterceptor 是 Spring Security 用于拦截请求进行权限鉴定的,其拥有两个具体的子类,拦截方法调用的 MethodSecurityInterceptor 和拦截 URL 请求的 FilterSecurityInterceptor。当 ExceptionTranslationFilter 捕获到的是 AuthenticationException 时将调用 AuthenticationEntryPoint 引导用户进行登陆;若是捕获的是 AccessDeniedException,可是用户尚未经过认证,则调用 AuthenticationEntryPoint 引导用户进行登陆认证,不然将返回一个表示不存在对应权限的 403 错误码。orm
可能你早就有这么一个疑问了,既然 SecurityContext 是存放在 ThreadLocal 中的,并且在每次权限鉴定的时候都是从 ThreadLocal 中获取 SecurityContext 中对应的 Authentication 所拥有的权限,而且不一样的 request 是不一样的线程,为何每次均可以从 ThreadLocal 中获取到当前用户对应的 SecurityContext 呢?在 Web 应用中这是经过 SecurityContextPersistentFilter 实现的,默认状况下其会在每次请求开始的时候从 session 中获取 SecurityContext,而后把它设置给 SecurityContextHolder,在请求结束后又会将 SecurityContextHolder 所持有的 SecurityContext 保存在 session 中,而且清除 SecurityContextHolder 所持有的 SecurityContext。这样当咱们第一次访问系统的时候,SecurityContextHolder 所持有的 SecurityContext 确定是空的,待咱们登陆成功后,SecurityContextHolder 所持有的 SecurityContext 就不是空的了,且包含有认证成功的 Authentication 对象,待请求结束后咱们就会将 SecurityContext 存在 session 中,等到下次请求的时候就能够从 session 中获取到该 SecurityContext 并把它赋予给 SecurityContextHolder 了,因为 SecurityContextHolder 已经持有认证过的 Authentication 对象了,因此下次访问的时候也就再也不须要进行登陆认证了。对象