Spring security securitycontextpersistencefilter and enterprise Redis use idea

Keywords: Redis Spring

In the previous articles, we basically described the implementation and source code analysis of spring security login authentication and authorization authentication. We are generally clear:

  1. SpringSecurity   Login authentication principle.
  2. If you customize the login authentication process
  3. Process principle of authorization verification

Today, let's analyze the whole authentication process and the idea of enterprise redis. What problems are we trying to solve and how to solve them?

Let's briefly review spring security   technological process.

  1. User login: enter user name / password
  2. JwtLoginFilter (user-defined UsernamePasswordAuthenticationFilter): intercept the login interface to authenticate the user's login information. Authentication successful: the information is stored in the context.
  3. Request again: enter the FilterSecurityInterceptor for authorization and authentication, and obtain the user permission information in the online SecurityContext and the permission comparison authentication required by the resources. If the authentication is passed, the resource will be accessed; otherwise, the exception information will be returned.

You can easily find that the above process requires a key context SecurityContext from beginning to end. It is handled by the SecurityContextPersistenceFilter filter. Let's first look at its implementation logic:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		if (request.getAttribute(FILTER_APPLIED) != null) {
			// ensure that filter is only applied once per request
			chain.doFilter(request, response);
			return;
		}


		final boolean debug = logger.isDebugEnabled();
		request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

		if (forceEagerSessionCreation) {
			HttpSession session = request.getSession();

			if (debug && session.isNew()) {
				logger.debug("Eagerly created session: " + session.getId());
			}
		}

		HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
				response);
        // Get context from
		SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

		try {
            // SecurityContextHolder
			SecurityContextHolder.setContext(contextBeforeChainExecution);

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

		}
		finally {
       
			SecurityContext contextAfterChainExecution = SecurityContextHolder
					.getContext();
            // Clear the SecurityContextHolder after authentication
			SecurityContextHolder.clearContext();
            // SecurityContextRepository into session
			repo.saveContext(contextAfterChainExecution, holder.getRequest(),
					holder.getResponse());
			request.removeAttribute(FILTER_APPLIED);

			if (debug) {
				logger.debug("SecurityContextHolder now cleared, as request processing completed");
			}
		}
	}

Explain what it means:

When requesting an API, perform the following steps:

1. Use the SecurityContextRepository#loadContext(holder) method to obtain the context of the currently logged in user. It has two implementation classes: HttpSessionSecurityContextRepository and NullSecurityContextRepository

HttpSessionSecurityContextRepository: gets the context from the session.

NullSecurityContextRepository: directly create an empty context. (it will be used when setting session to stateless)

2. set the obtained context to SecurityContextHolder#setContext. SecurityContextHolder is the class for our whole process to obtain context data, so we get the context through the session at the beginning and put it into the SecurityContextHolder for use in subsequent authentication processes.

3. Directly call the next filter of doFilter, that is, enter the verification process.

4. After the authentication request is completed, clear the SecurityContextHolder and save the context up and down   The SecurityContextRepository is saved in the session.

Through the above process, we know several points:

SecurityContextHolder is the holder of context information in the whole authentication process, but it will be cleared after authentication.

The context information is managed by the SecurityContextRepository. Each request will obtain the context from the session in advance or directly empty the context for subsequent use. If the context is logged in, there will be information, otherwise the subsequent verification will fail.

After each authentication request, the SecurityContextHolder will be cleared and coexisted in the session. The next time I come, I'll get it from the session.


However, for enterprise projects, there are too many deficiencies:

one   As long as the user logs in, accessing any api is to obtain the current information from the session, so the security of the api itself is low. What we need is that each api should be stateless. You are required to conduct security authentication (token) for each api request.

two   If the user information is stored in the session, the session will become more and more large with too much login authentication, and the memory can't afford it.

three   The session is based on the jvm memory, and the service can't be restarted.

Therefore, we need to store token data based on cache, such as redis. The general idea is as follows:

First, turn off the spring security session management and require each request to be verified,   Implement a filter to verify the token in the request header. If the authentication passes, an authenticationToken is encapsulated to the context for subsequent authentication. If it does not pass, there is no data in the context at this time, and subsequent filters will naturally intercept it.

Note: Spring security   After session management, spring security will skip all sessions   filter chain: HttpSessionSecurityContextRepository,   SessionManagementFilter,   RequestCacheFilter

Posted by eideticmnemonic on Fri, 17 Sep 2021 22:23:00 -0700