springboot服务认证方式Tomcat中session创建管理流程分析

本文介绍springboot项目采用spring-security认证,传递token,session的生成流程,分析源码创建过程。

最主要的入口在org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter#doFilter

这个请求过滤器中。具体访问流程如下

Tomcat接收请求进入org.apache.catalina.authenticator.AuthenticatorBase#invoke这个方法。

流程进入下一个过滤器:

org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter#doFilter

public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
			ServletException {

		final boolean debug = logger.isDebugEnabled();
		final HttpServletRequest request = (HttpServletRequest) req;
		final HttpServletResponse response = (HttpServletResponse) res;

		try {
// 提取token值封装到PreAuthenticatedAuthenticationToken类中
			Authentication authentication = tokenExtractor.extract(request);
			
			if (authentication == null) {
//isAuthenticated()是否认证通过
				if (stateless && isAuthenticated()) {
					if (debug) {
						logger.debug("Clearing security context.");
					}
//    清除context
					SecurityContextHolder.clearContext();
				}
				if (debug) {
					logger.debug("No token in request, will continue chain.");
				}
			}
			else {
//设置request属性,token值
				request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
				if (authentication instanceof AbstractAuthenticationToken) {
					AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
					needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
				}
// 去调用 用户服务认证token信息。返回认证结果
				Authentication authResult = authenticationManager.authenticate(authentication);

				if (debug) {
					logger.debug("Authentication success: " + authResult);
				}
//发布认证结果事件
				eventPublisher.publishAuthenticationSuccess(authResult);
				SecurityContextHolder.getContext().setAuthentication(authResult);

			}
		}
		catch (OAuth2Exception failed) {
			SecurityContextHolder.clearContext();

			if (debug) {
				logger.debug("Authentication request failed: " + failed);
			}
			eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
					new PreAuthenticatedAuthenticationToken("access-token", "N/A"));

			authenticationEntryPoint.commence(request, response,
					new InsufficientAuthenticationException(failed.getMessage(), failed));

			return;
		}

		chain.doFilter(request, response);
	}
}

再进入org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager#authenticate

springboot服务认证方式Tomcat中session创建管理流程分析

 

public class OAuth2AuthenticationManager implements AuthenticationManager, InitializingBean {
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {

		if (authentication == null) {
			throw new InvalidTokenException("Invalid token (token not found)");
		}
		String token = (String) authentication.getPrincipal();
// 访问认证服务,返回认证用户信息
		OAuth2Authentication auth = tokenServices.loadAuthentication(token);
		if (auth == null) {
			throw new InvalidTokenException("Invalid token: " + token);
		}

		Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
		if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
			throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
		}

		checkClientDetails(auth);

		if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
			OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
			// Guard against a cached copy of the same details
			if (!details.equals(auth.getDetails())) {
				// Preserve the authentication details from the one loaded by token services
				details.setDecodedDetails(auth.getDetails());
			}
		}
		auth.setDetails(authentication.getDetails());
//设置认证成功true
		auth.setAuthenticated(true);
		return auth;

	}
}

springboot服务认证方式Tomcat中session创建管理流程分析

springboot服务认证方式Tomcat中session创建管理流程分析 

 上边2张图是认证失败的,token过期了,

再次获取token。

认证成功后进入org.springframework.security.web.session.SessionManagementFilter#doFilter

这个类是管理session的

public class SessionManagementFilter extends GenericFilterBean {
    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) {
			chain.doFilter(request, response);
			return;
		}

		request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

		if (!securityContextRepository.containsContext(request)) {
//获取上次认证成功返回的信息,这里存入了ThreadLocal中
			Authentication authentication = SecurityContextHolder.getContext()
					.getAuthentication();

			if (authentication != null && !trustResolver.isAnonymous(authentication)) {
				// The user has been authenticated during the current request, so call the
				// session strategy
				try {
//执行session管理策略,配置在
//org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer这个类中
					sessionAuthenticationStrategy.onAuthentication(authentication,
							request, response);
				}
				catch (SessionAuthenticationException e) {
					// The session strategy can reject the authentication
					logger.debug(
							"SessionAuthenticationStrategy rejected the authentication object",
							e);
					SecurityContextHolder.clearContext();
					failureHandler.onAuthenticationFailure(request, response, e);

					return;
				}
				// Eagerly save the security context to make it available for any possible
				// re-entrant
				// requests which may occur before the current request completes.
				// SEC-1396.
				securityContextRepository.saveContext(SecurityContextHolder.getContext(),
						request, response);
			}
			else {
				// No security context or authentication present. Check for a session
				// timeout
				if (request.getRequestedSessionId() != null
						&& !request.isRequestedSessionIdValid()) {
					if (logger.isDebugEnabled()) {
						logger.debug("Requested session ID "
								+ request.getRequestedSessionId() + " is invalid.");
					}

					if (invalidSessionStrategy != null) {
						invalidSessionStrategy
								.onInvalidSessionDetected(request, response);
						return;
					}
				}
			}
		}

		chain.doFilter(request, response);
	}
}

首先进入session的组合管理策略,在进入自己配置的 

org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy#onAuthentication

     再进入org.springframework.web.servlet.DispatcherServlet#doService

session的生成类org.apache.catalina.util.StandardSessionIdGenerator

session配置类

public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {
	private final SessionAuthenticationStrategy DEFAULT_SESSION_FIXATION_STRATEGY = createDefaultSessionFixationProtectionStrategy();
//默认的session策略是ChangeSessionIdAuthenticationStrategy,每次都会生成一个新的
	private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = this.DEFAULT_SESSION_FIXATION_STRATEGY;
	private SessionAuthenticationStrategy sessionAuthenticationStrategy;
	private SessionAuthenticationStrategy providedSessionAuthenticationStrategy;
//失效session策略
	private InvalidSessionStrategy invalidSessionStrategy;
	private SessionInformationExpiredStrategy expiredSessionStrategy;
	private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<>();
	private SessionRegistry sessionRegistry;
//允许的最大session数量
	private Integer maximumSessions;
	private String expiredUrl;
	private boolean maxSessionsPreventsLogin;
	private SessionCreationPolicy sessionPolicy = SessionCreationPolicy.IF_REQUIRED;
	private boolean enableSessionUrlRewriting;
//session失效需要跳转地址
	private String invalidSessionUrl;
	private String sessionAuthenticationErrorUrl;
	private AuthenticationFailureHandler sessionAuthenticationFailureHandler;
}

上一篇:从零设计开发快递自动填写收发货地址功能


下一篇:腾讯地图类快递自动填写收发货地址功能