Analysis of Spring Security Authentication System

Spring Security is a well-known security framework, and also provides a complete set of Web-based authentication mechanisms and security services. Of course, if you want to implement security services through other protocols, you can also use Spring Security to help you.
 
Today, I mainly explain the security authentication mechanism based on the web side, and I will keep the others and organize them into blog posts when I have time.
 
First of all, SpringSecurity Web is implemented based on a complete set of Filter chains it provides. Let's take a look at what Filters it provides:


 
As you can see in the list, these Filter chains are ordered , and the Filter provided by itself cannot change the order, so what? ? ? Of course, you can change the order arbitrarily, so how can I guarantee that I get the correct data in different filters, right? These filters are injected into the container by default by Spring Security at startup. You can insert your own filters before or after these filters according to your own business needs.
 
What exactly does each filter do, you can check it yourself. This is not the focus of today's discussion. Today, we will focus on the web-based authentication mechanism.
 
Let's take a look at several key classes for authentication in Spring Security:
org.springframework.security.web.context.SecurityContextRepository
 
org.springframework.security.web.context.SecurityContextPersistenceFilter
 
org.springframework.security.core.context.SecurityContextHolder
 
org.springframework.security.core.context.SecurityContextHolderStrategy
 
org . springframework . security . core .Authentication
 
First of all, let's take a look at the interaction diagram as a user when logging in. This diagram is indeed too big and has been reduced as much as possible, but it is still very large.


 
 
From the figure, we can see that when a user requests a service, they will first go through the SecurityContextPersistenceFilter in the figure (in fact, this class is processed second in the Filter chain). What is this class for? It is mainly used for Set SecurityContext to SecurityContextHolder. Specifically, we analyze it a little bit through the diagram and source code
 
First look at SecurityContextPersistenceFilter.class
public SecurityContextPersistenceFilter() {
	this(new HttpSessionSecurityContextRepository());
}

public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
	this.repo = repo;
}
 
 
We can see that the constructor of this class needs to set SecurityContextRepository, which will be set to HttpSessionSecurityContextRepository.class by default . What is this thing used for? It is used to set the storage mechanism of SecurityContext. This class plays a key role in whether you can get the correct context every time you use SecurityContextHolder to getContext. You can see that HttpSession is used by default to implement the storage of SecutiryContext.
 
Next, let's see what's going on in doFilter:
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,response);
// Here you can see that the SecurityContext is obtained from the repo, and the default is obtained from the HttpSession
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

try {
// At the same time, set the created SecurityContext to SecurityContextHolder here SecurityContextHolder.setContext(contextBeforeChainExecution);

chain.doFilter(holder.getRequest(), holder.getResponse());

}
 
Let's take a look at the implementation of repo.loadCotext.
// First read from the Session whether there is a SecurityContext
SecurityContext context = readSecurityContextFromSession(httpSession);
// Creates an empty SecurityContext if it doesn't exist
if (context == null) {
    context = generateNewContext();
}

return context;

protected SecurityContext generateNewContext() {
    return SecurityContextHolder.createEmptyContext();
}

private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
    Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);
}
 
在这里可以看到如果Session是一个新的话,这里的ScurityContext会从SecurityContextHolder中去创建,在SecurityContextHolder中可以看到默认会通过ThreadLocalSecurityContextHolderStrategy创建一个基于ThreadLocal存储线程安全的SecurityContext,那当前线程中所拿到的SecurityContext均是同一个实例。

现在知道了SecurityContext是如何创建了之后,再回头来看SecurityContextPersistenceFilter中的doFilter实现
try {
// 在这里将创建好的SecurityContext设置到SecurityContextHolder中			 
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());

}
 
现在已经在chain.doFilter()之前已经完成了对SecurityContext的设置,接下来过滤器链继续执行,如果你在配置SecurityConfig中使用了UsernamePasswordAuthenticationFilter.class来实现对用户的认证,那我们将会进入到UsernamePasswordAuthenticationFilter的doFilter来实现认证
Authentication authResult;

try {
	authResult = attemptAuthentication(request, response);
	if (authResult == null) {
		// return immediately as subclass has indicated that it hasn't completed
		// authentication
		return;
	}
	sessionStrategy.onAuthentication(authResult, request, response);
}
successfulAuthentication(request, response, chain, authResult);

protected void successfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain, Authentication authResult)
			throws IOException, ServletException {

		if (logger.isDebugEnabled()) {
			logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
					+ authResult);
		}

		SecurityContextHolder.getContext().setAuthentication(authResult);

		rememberMeServices.loginSuccess(request, response, authResult);

		// Fire event
		if (this.eventPublisher != null) {
			eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
					authResult, this.getClass()));
		}

		successHandler.onAuthenticationSuccess(request, response, authResult);
	}
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}

		String username = obtainUsername(request);
		String password = obtainPassword(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim();

		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);

		return this.getAuthenticationManager().authenticate(authRequest);
	}
  
 
一旦attemptAuthentication认证通过之后,可以在successfulAuthentication方法中可以看到调用了SecurityContextHolder.getContext().setAuthentication(authResult)来完成对用户的认证,到这里已经完成了基于SpringSecurity所提供的一套web完整的认证过程。
 
在这里就不对UsernamePasswordAuthenticationFilter里的认证机制做详细的阐述了,大家有兴趣的话自己去翻源码查看吧。还是比较简单里,里面设计到了AuthenticationManager、AuthenticationProvider、UserDetailsService等相关的类。
 
如果你使用的restful接口来实现认证的话,你也可以在验证之后构造一个Authentication实例,再通过 SecurityContextHolder.getContext().setAuthentication( authentication )来实现对SpringSecurity的认证。
 
讲到这里大家对spring security的认证机制应该有一定的认识了吧,如果有什么讲得不到位的地方,欢迎大家交流~

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326552093&siteId=291194637