在spring security中认证具体指的是什么以及如何做的,本文做一个简单的阐述
环境
spring boot 版本:1.5.4.RELEASE
下面我们一起考虑一个简单的大家都熟悉的认证情景
- 1.一个用户被提示输入用户名和密码
- 2.系统验证用户名和密码是否合法
- 3.用户的上下文信息被获取(如用户的权限列表)
- 4.创建用户对应的安全上下文
- 5.当用户继续执行后续操作时,系统的安全访问控制机制利用当前的安全上下文信息来判断这些操作是否被许可
上面情景中的前三步就是基本的认证过程,下面让我们看下这在spring security中是如何发生的。
- 1.从httpRequest中获取username和password,然后组合成一个实现了Authentication接口的UsernamePasswordAuthenticationToken实例
- 2.组合成的token被传递给一个AuthenticationManager实例(ProviderManager)来验证
- 3.验证成功AuthenticationManager会返回一个组装好的Authentication实例(包含用户的权限信息)
- 4.通过调用SecurityContextHolder.getContext().setAuthentication(...)方法,并传入组装好的Authentication实例来创建安全上下文
之后,当前用户就完成了认证。此过程,可以有如下代码来模拟
import org.springframework.security.authentication.*; import org.springframework.security.core.*; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; public class AuthenticationExample { private static AuthenticationManager am = new SampleAuthenticationManager(); public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); while(true) { System.out.println("Please enter your username:"); String name = in.readLine(); System.out.println("Please enter your password:"); String password = in.readLine(); try { Authentication request = new UsernamePasswordAuthenticationToken(name, password); Authentication result = am.authenticate(request); SecurityContextHolder.getContext().setAuthentication(result); break; } catch(AuthenticationException e) { System.out.println("Authentication failed: " + e.getMessage()); } } System.out.println("Successfully authenticated. Security context contains: " + SecurityContextHolder.getContext().getAuthentication()); } } class SampleAuthenticationManager implements AuthenticationManager { static final ListAUTHORITIES = new ArrayList (); static { AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER")); } public Authentication authenticate(Authentication auth) throws AuthenticationException { if (auth.getName().equals(auth.getCredentials())) { return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), AUTHORITIES); } throw new BadCredentialsException("Bad Credentials"); } }
其中SampleAuthenticationManager是我们实现的一个认证类,只是简单判断如果用户名和密码相同就认证成功,并赋给用户一个固定的USER权限。运行后程序的输出如下
Please enter your username: bob Please enter your password: password Authentication failed: Bad Credentials Please enter your username: bob Please enter your password: bob Successfully authenticated. Security context contains: \ org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: \ Principal: bob; Password: [PROTECTED]; \ Authenticated: true; Details: null; \ Granted Authorities: ROLE_USER
在实际应用中,我们也不会直接这样写代码,这里我们只是为了说明spring security的认证过程。这个例子也说明在spring security中如何判断认证成功,那就是SecurityContextHolder中包含一个完全组装好的Authentication对象。
这个例子也说明了spring security并不关心Authentication对象是如何存放到SecurityContextHolder中的,唯一需要确认的就是在AbstractSecurityInterceptor要鉴权一个用户时SecurityContextHolder中包含一个Authentication对象就OK了。