最近在学习spring security,想着用最近积累的知识写成第一篇文章,也是对自己学习的一种总结。(第一篇简书,大神们多多指教。。)
先说说自己对spring security的认识。
1. 什么是spring security?
Spring security 是一个基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。Spring Security的Web基础设施完全基于标准的servlet过滤器,里面涉及了一连串的Fiter过滤器链。它不在内部使用servlet或任何基于servlet的框架(如Spring MVC),它处理请求和响应,并不会关心这些请求和响应来自哪里(比如浏览器,Web服务客户端HttpInvoker还是AJAX应用程序)。
2.核心组件
2.1 SecurityContextHolder
最根本的对象是SecurityContextHolder,它用于存储安全上下文的详细信息(比如当前操作的用户是谁,该用户是否被认证,他拥有哪些权限……)。它默认使用ThreadLocal存储这些详细信息,这意味着Spring Security 在用户登录时自动绑定认证信息到当前线程。在用户退出时,它会自动清除当前线程的认证信息(目前只针对web场景,官方文档提供了Swing的例子)。
获取有关当前用户的信息
Spring Security使用Authentication对象来表示此信息。您通常不需要自己创建一个Authentication对象,但是用户查询Authentication对象是相当普遍的。您可以使用以下代码(从应用程序的任何位置)获取当前验证的用户的名称:
获取用户身份信息
getAuthentication()返回了认证信息,再次getPrincipal()返回了身份信息,UserDetails便是Spring对身份信息封装的一个接口。
2.2 Authentication
AuthenticationAuthentication在spring security中是最高级别的身份/认证的抽象,从这个对象里面我们可以得到用户拥有的:
- 权限信息列表,默认为GrantedAuthority接口的一些实现类,通常是代表权限信息的一系列字符串。
- 密码, 用户输入的密码,在认证完成后通常会被移除,用于保障安全。
- 用户明细信息,通常记录了访问者的ip地址和SessionId的值
- 用户身份 ,大部分情况下返回的是userDetails接口的实现类。
- 是否已经验证的状态。
2.3 UserDetails
UserDetails它和Authentication接口很类似,比如它们都拥有username,authorities,Authentication的getCredentials()是用户输入的密码,后者是用户正确的密码,认证器其实就是对这两者的对比。Authentication.getAuthorities()实际上是由UserDetails.getAuthorities()传递形成的。Authentication.getDetails()是经过AuthenticationProvider验证之后,copyDetails方法填充进去的。
Spring Security是如何完成身份认证的?
标准的认证场景:
- 提示用户使用用户名和密码登录。
- 验证用户名和密码是否正确。
- 获取该用户的上下文信息(其角色列表等)。
- 为用户建立安全上下文
- 用户访问可能有访问控制机制保护的一些操作,该访问控制机制根据当前的安全上下文信息检查该操作所需的权限。
spring security是这么实现的:
- 用户名和密码被过滤器获取到,封装成Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。
- AuthenticationManager身份管理器负责验证这个Authentication。
- 认证成功后,AuthenticationManager身份管理器返回一个被填充满了信息的Authentication的实例。
- SecurityContextHolder安全上下文容器通过调用SecurityContextHolder.getContext().setAuthentication(…)填充。
接下来看官方文档提供的例子:
AuthenticationExample当然这只是一个简单描述流程的例子。在实际使用中,整个流程我们通常放web过滤器中,会变得更加的复杂,但基本思想跟如上代码如出一辙。
2.4 AuthenticationManager,ProviderManager和AuthenticationProvider
AuthenticationManager(接口)是认证相关的核心接口,也是发起认证的出发点,AuthenticationManager一般不直接认证,而是通过常用实现类ProviderManager委派给已配置AuthenticationProvider的列表去处理身份验证。因为在实际需求中,我们可能同时允许用户名+密码登录,邮箱+密码登录,手机号码+密码登录,指纹登录,核心的认证入口始终只是AuthenticationManager,不同的认证方式:用户名+密码登录(UsernamePasswordAuthenticationProvider),邮箱+密码,手机号码+密码登录则对应了三个AuthenticationProvider。(当然还有RememberMeAuthenticationProvider,AnonymousAuthenticationProvider等AuthenticationProvider的一些实现类。)
2.5 DaoAuthenticationProvider
AuthenticationProvider最常用的一个实现便是DaoAuthenticationProvider (AuthenticationProvider->AbstractUserDetailsAuthenticationProvider->DaoAuthenticationProvider)。用户前台提交了用户名和密码,被封装成了UsernamePasswordAuthenticationToken, 另一方面DaoAuthenticationProvider通过retrieveUser方法从UserDetailService加载输入的用户名所对应正确的用户信息(比如通过查询数据库),这个方法返回一个UserDetails。 DaoAuthenticationProvider中additionalAuthenticationChecks方法完成的,该方法没有返回值,如果没有抛异常,则认为密码正确了。 对比密码过程中,用到了PasswordEncoder(加密)和SaltSource(加盐),保证安全。
2.6 UserDetailsService
UserDetailsService中,只有loadUserByUsername方法,它只负责从特地的地方加载用户信息。它的常见实现类有JdbcDaoImpl,InMemoryUserDetailsManager,前者从数据库加载用户,后者从内存中加载用户,通常都自己实现UserDetailsService,这样比较灵活。
大致的流程都有了,还有其中过滤器的流程和其中细节还没到位(如封装UsernamePasswordAuthenticationToken的过滤器),之后有时间再补上。。
网友评论