8.1.3 Authentication
Spring Security can participate in many different authentication environments. While we recommend people use Spring Security for authentication and not integrate with existing Container Managed Authentication, it is nevertheless supported - as is integrating with your own proprietary authentication system.
Spring Security可以参与许多不同的身份验证环境。虽然我们建议人们使用Spring Security进行身份验证,而不是与现有的容器管理身份验证集成,但是它仍然受到支持——就像与您自己的专有身份验证系统集成一样。
What is authentication in Spring Security?
Let’s consider a standard authentication scenario that everyone is familiar with.
1, A user is prompted to log in with a username and password.
2, The system (successfully) verifies that the password is correct for the username.
3, The context information for that user is obtained (their list of roles and so on).
4, A security context is established for the user
5, The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information.
让我们考虑一个每个人都熟悉的标准身份验证场景。
1, 提示用户使用用户名和密码登录。
2, 系统(成功)验证用户名的密码是否正确。
3, 获取该用户的上下文信息(角色列表等)。
4, 为用户建立一个安全上下文
5, 用户继续执行某些操作,这些操作可能受到访问控制机制的保护,该机制根据当前安全上下文信息检查操作所需的权限。
The first three items constitute the authentication process so we’ll take a look at how these take place within Spring Security.
1, The username and password are obtained and combined into an instance of UsernamePasswordAuthenticationToken (an instance of the Authentication interface, which we saw earlier).
2, The token is passed to an instance of AuthenticationManager for validation.
3, The AuthenticationManager returns a fully populated Authentication instance on successful authentication.
4, The security context is established by calling SecurityContextHolder.getContext().setAuthentication(…), passing in the returned authentication object.
前三项构成了身份验证过程,因此我们将了解这些在Spring Security中是如何发生的。
1, 用户名和密码被获取并组合到UsernamePasswordAuthenticationToken的实例中(Authenticationinterface的实例,我们在前面看到过)。
2, 令牌传递给AuthenticationManager的一个实例进行验证。
3, AuthenticationManager在身份验证成功时返回一个完整填充的身份验证实例。
4, 安全上下文是通过调用securitycontext.getcontext().setauthentication(…),传入返回的身份验证对象来建立的。
From that point on, the user is considered to be authenticated. Let’s look at some code as an example.
从那时起,用户被认为是经过身份验证的。让我们以一些代码为例。
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 List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
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");
}
}
Here we have written a little program that asks the user to enter a username and password and performs the above sequence. The AuthenticationManager which we’ve implemented here will authenticate any user whose username and password are the same. It assigns a single role to every user. The output from the above will be something like:
在这里,我们编写了一个小程序,要求用户输入用户名和密码并执行上面的顺序。我们在这里实现的AuthenticationManager将对用户名和密码相同的任何用户进行身份验证。它为每个用户分配一个角色。上面的输出将类似于:
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
Note that you don’t normally need to write any code like this. The process will normally occur internally, in a web authentication filter for example. We’ve just included the code here to show that the question of what actually constitutes authentication in Spring Security has quite a simple answer. A user is authenticated when the SecurityContextHolder
contains a fully populated Authentication
object.
请注意,通常不需要编写这样的代码。这个过程通常发生在内部,例如在web身份验证过滤器中。我们刚刚在这里包含了一些代码,以说明在Spring Security中真正构成身份验证的问题有一个非常简单的答案。当SecurityContextHolder
包含一个完全填充的身份验证对象时,对用户进行身份验证。
Setting the SecurityContextHolder Contents Directly
In fact, Spring Security doesn’t mind how you put the Authentication
object inside the SecurityContextHolder
. The only critical requirement is that the SecurityContextHolder
contains an Authentication
which represents a principal before the AbstractSecurityInterceptor
(which we’ll see more about later) needs to authorize a user operation.
You can (and many users do) write their own filters or MVC controllers to provide interoperability with authentication systems that are not based on Spring Security. For example, you might be using Container-Managed Authentication which makes the current user available from a ThreadLocal or JNDI location. Or you might work for a company that has a legacy proprietary authentication system, which is a corporate "standard" over which you have little control. In situations like this it’s quite easy to get Spring Security to work, and still provide authorization capabilities. All you need to do is write a filter (or equivalent) that reads the third-party user information from a location, build a Spring Security-specific Authentication
object, and put it into the SecurityContextHolder
. In this case you also need to think about things which are normally taken care of automatically by the built-in authentication infrastructure. For example, you might need to pre-emptively create an HTTP session to cache the context between requests, before you write the response to the client footnote:[It isn’t possible to create a session once the response has been committed.
实际上,Spring Security并不介意您如何将身份验证对象放入SecurityContextHolder
中。惟一的关键需求是,SecurityContextHolder
包含一个身份验证,在AbstractSecurityInterceptor(稍后将详细介绍)需要授权用户操作之前,该身份验证代表一个主体。
您可以(许多用户也可以)编写自己的过滤器或MVC控制器,以提供与不基于Spring安全性的身份验证系统的互操作性。例如,您可能正在使用容器管理的身份验证,这使得当前用户可以从ThreadLocal或JNDI位置访问。或者,您可能为一家拥有遗留专有身份验证系统的公司工作,该系统是一个您几乎无法控制的公司“标准”。在这种情况下,很容易让Spring Security工作,并且仍然提供授权功能。您所需要做的就是编写一个过滤器(或等效的过滤器),从一个位置读取第三方用户信息,构建一个Spring特定于安全的身份验证对象,并将其放入SecurityContextHolder
中。在这种情况下,您还需要考虑通常由内置身份验证基础设施自动处理的事情。例如,您可能需要先创建一个HTTP会话,以便在请求之间缓存上下文,然后再编写对客户机脚注的响应:[不可能在提交响应之后创建会话。
If you’re wondering how the AuthenticationManager
is implemented in a real world example, we’ll look at that in the core services chapter.
如果您想知道AuthenticationManager
在实际示例中是如何实现的,我们将在核心服务一章中对此进行介绍。