spring security(学习一)

Authentication 是一个接口,用来表示用户认证信息的,在用户登陆认证以前相关信息会封装为一个 Authentication 具体实现类的对象,在登陆认证成功以后又会生成一个信息更全面,包含用户权限等信息的 Authentication 对象,而后把它保存在 SecurityContextHolder 所持有的 SecurityContext 中,供后续的程序进行调用,如访问权限的鉴定等。spring

认证流程:用户登录时AuthenticationProcessingFilter会拦截请求,调用AuthenticationManager(AuthenticationManager 是一个用来处理认证(Authentication)请求的接口。在其中只定义了一个方法 authenticate()),AuthenticationManager的默认实现是ProviderManager,而且ProviderManager不处理,它交给它配置的AuthenticationProvider 列表来处理,而后依次的调用每一个AuthenticationProvider 来认证,只要有一个AuthenticationProvider认证的结果不为空,则认证成功,将其结果做为ProviderManager 的认证结果。若是全部的AuthenticationProvider的结果都为空,那么认证失败,抛出ProviderNotFoundException。数据库

  1. 用户使用用户名和密码进行登陆。
  2. Spring Security 将获取到的用户名和密码封装成一个实现了 Authentication 接口的 UsernamePasswordAuthenticationToken。
  3. 将上述产生的 token 对象传递给 AuthenticationManager 进行登陆认证。
  4. AuthenticationManager 认证成功后将会返回一个封装了用户权限等信息的 Authentication 对象。
  5. 经过调用 SecurityContextHolder.getContext().setAuthentication(...) 将 AuthenticationManager 返回的 Authentication 对象赋予给当前的 SecurityContext。

Web 应用的认证过程

若是用户直接访问登陆页面,那么认证过程跟上节描述的基本一致,只是在认证完成后将跳转到指定的成功页面,默认是应用的根路径。若是用户直接访问一个受保护的资源,那么认证过程将以下:缓存

  1. 引导用户进行登陆,一般是重定向到一个基于 form 表单进行登陆的页面,具体视配置而定。
  2. 用户输入用户名和密码后请求认证,后台仍是会像上节描述的那样获取用户名和密码封装成一个 UsernamePasswordAuthenticationToken 对象,而后把它传递给 AuthenticationManager 进行认证。
  3. 若是认证失败将继续执行步骤 1,若是认证成功则会保存返回的 Authentication 到 SecurityContext,而后默认会将用户重定向到以前访问的页面。
  4. 用户登陆认证成功后再次访问以前受保护的资源时就会对用户进行权限鉴定,如不存在对应的访问权限,则会返回 403 错误码。

在上述步骤中将有不少不一样的类参与,但其中主要的参与者是 ExceptionTranslationFilter。安全

校验认证请求最经常使用的方法是根据请求的用户名加载对应的 UserDetails,而后比对 UserDetails 的密码与认证请求的密码是否一致,一致则表示认证经过。Spring Security 内部的 DaoAuthenticationProvider 就是使用的这种方式。session

UserDetailsService 来负责加载 UserDetails,在认证成功之后会使用加载的 UserDetails 来封装要返回的 Authentication 对象,加载的 UserDetails 对象是包含用户权限等信息的。认证成功返回的 Authentication 对象将会保存在当前的 SecurityContext 中。框架

使用NameSpace时, authentication-manager 元素的使用会使 Spring Security 在内部建立一个 ProviderManager,而后能够经过 authentication-provider 元素往其中添加 AuthenticationProvider。当定义 authentication-provider 元素时,若是没有经过 ref 属性指定关联哪一个 AuthenticationProvider,Spring Security 默认就会使用 DaoAuthenticationProvider。使用了 NameSpace 后咱们就不要再声明 ProviderManager 了。ide

 

<security:authentication-manager alias="authenticationManager">
      <security:authentication-provider
         user-service-ref="userDetailsService"/>
   </security:authentication-manager>

若是没有使用NameSpace,咱们要在ApplicationContext中声明一个ProviderManager。spa

默认状况下,在认证成功后 ProviderManager 将清除返回的 Authentication 中的凭证信息,如密码。因此若是你在无状态的应用中将返回的 Authentication 信息缓存起来了,那么之后你再利用缓存的信息去认证将会失败,由于它已经不存在密码这样的凭证信息了。因此在使用缓存的时候你应该考虑到这个问题。一种解决办法是设置 ProviderManager 的 eraseCredentialsAfterAuthentication 属性为 false,或者想办法在缓存时将凭证信息一块儿缓存。线程

经过 Authentication.getPrincipal() 的返回类型是 Object,但不少状况下其返回的实际上是一个 UserDetails 的实例。UserDetails是Spring Security里面的核心接口,封装了得到认证信息的方法。登陆认证的时候 Spring Security 会经过 UserDetailsService 的 loadUserByUsername() 方法获取对应的 UserDetails 进行认证,认证经过后会将该 UserDetails 赋给认证经过的 Authentication 的 principal,而后再把该 Authentication 存入到 SecurityContext 中。以后若是须要使用用户信息的时候就是经过 SecurityContextHolder 获取存放在 SecurityContext 中的 Authentication 的 principal。code

<!-- 用于认证的 AuthenticationManager -->
   <security:authentication-manager alias="authenticationManager">
      <security:authentication-provider
         user-service-ref="userDetailsService" />
   </security:authentication-manager>

   <bean id="userDetailsService"
      class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
      <property name="dataSource" ref="dataSource" />
   </bean>

上述代码中咱们使用的 JdbcDaoImpl 是 Spring Security 为咱们提供的 UserDetailsService 的实现,另外 Spring Security 还为咱们提供了 UserDetailsService 另一个实现,InMemoryDaoImpl。

其做用是从数据库中加载 UserDetails 信息。其中已经定义好了加载相关信息的默认脚本,这些脚本也能够经过 JdbcDaoImpl 的相关属性进行指定。关于 JdbcDaoImpl 使用方式会在讲解 AuthenticationProvider 的时候作一个相对详细一点的介绍。

 

模块划分

  • Web/Http 安全:这是最复杂的部分。经过创建 filter 和相关的 service bean 来实现框架的认证机制。当访问受保护的 URL 时会将用户引入登陆界面或者是错误提示界面。
  • 业务对象或者方法的安全:控制方法访问权限的。
  • AuthenticationManager:处理来自于框架其余部分的认证请求。
  • AccessDecisionManager:为 Web 或方法的安全提供访问决策。会注册一个默认的,可是咱们也能够经过普通 bean 注册的方式使用自定义的 AccessDecisionManager。
  • AuthenticationProvider:AuthenticationManager 是经过它来认证用户的。
  • UserDetailsService:跟 AuthenticationProvider 关系密切,用来获取用户信息的。

在 request 之间共享 SecurityContext

可能你早就有这么一个疑问了,既然 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 对象了,因此下次访问的时候也就再也不须要进行登陆认证了。

相关文章
相关标签/搜索