spring实战----security4.1.3认证的过程以及原请求信息的缓存及恢复(requestcache)(代码片段)

Herman-Hong Herman-Hong     2022-11-29     669

关键词:

一、先看下认证过程

认证过程分为7步:
1.用户访问网站,打开了一个链接(origin url)。


2.请求发送给服务器,服务器判断用户请求了受保护的资源。

3.由于用户没有登录,服务器重定向到登录页面

4.填写表单,点击登录

5.浏览器将用户名密码以表单形式发送给服务器

6.服务器验证用户名密码。成功,进入到下一步。否则要求用户重新认证(第三步)

7.服务器对用户拥有的权限(角色)判定: 有权限,重定向到origin url; 权限不足,返回状态码403("forbidden").

从第3步,我们可以知道,用户的请求被中断了。

用户登录成功后(第7步),会被重定向到origin url,spring security通过使用缓存的request,使得被中断的请求能够继续执行。


二、使用缓存


用户登录成功后,页面重定向到origin url。浏览器发出的请求优先被拦截器RequestCacheAwareFilter拦截,RequestCacheAwareFilter通过其持有的RequestCache对象实现request的恢复。

public void doFilter(ServletRequest request, ServletResponse response,
 FilterChain chain) throws IOException, ServletException 

 // request匹配,则取出,该操作同时会将缓存的request从session中删除
HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest(
 (HttpServletRequest) request, (HttpServletResponse) response);

 // 优先使用缓存的request
 chain.doFilter(wrappedSavedRequest == null ? request : wrappedSavedRequest,
 response);
 


三、何时缓存


首先,我们需要了解下RequestCache以及ExceptionTranslationFilter。

1)RequestCache

RequestCache接口声明了缓存与恢复操作。默认实现类是HttpSessionRequestCache。接口的声明如下:

public interface RequestCache 

 // 将request缓存到session中
void saveRequest(HttpServletRequest request, HttpServletResponse response);

 // 从session中取request
 SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);

 // 获得与当前request匹配的缓存,并将匹配的request从session中删除
HttpServletRequest getMatchingRequest(HttpServletRequest request,
 HttpServletResponse response);

 // 删除缓存的request
 void removeRequest(HttpServletRequest request, HttpServletResponse response);

实现类为HttpSessionRequestCache

public class HttpSessionRequestCache implements RequestCache 
	static final String SAVED_REQUEST = "SPRING_SECURITY_SAVED_REQUEST";
	protected final Log logger = LogFactory.getLog(this.getClass());

	private PortResolver portResolver = new PortResolverImpl();
	private boolean createSessionAllowed = true;
	private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;

	/**
	 * Stores the current request, provided the configuration properties allow it.
	 */
	public void saveRequest(HttpServletRequest request, HttpServletResponse response) 
		if (requestMatcher.matches(request)) 
			DefaultSavedRequest savedRequest = new DefaultSavedRequest(request,
					portResolver);

			if (createSessionAllowed || request.getSession(false) != null) 
				// Store the HTTP request itself. Used by
				// AbstractAuthenticationProcessingFilter
				// for redirection after successful authentication (SEC-29)
				request.getSession().setAttribute(SAVED_REQUEST, savedRequest);
				logger.debug("DefaultSavedRequest added to Session: " + savedRequest);
			
		
		else 
			logger.debug("Request not saved as configured RequestMatcher did not match");
		
	

	public SavedRequest getRequest(HttpServletRequest currentRequest,
			HttpServletResponse response) 
		HttpSession session = currentRequest.getSession(false);

		if (session != null) 
			return (SavedRequest) session.getAttribute(SAVED_REQUEST);
		

		return null;
	

	public void removeRequest(HttpServletRequest currentRequest,
			HttpServletResponse response) 
		HttpSession session = currentRequest.getSession(false);

		if (session != null) 
			logger.debug("Removing DefaultSavedRequest from session if present");
			session.removeAttribute(SAVED_REQUEST);
		
	

	public HttpServletRequest getMatchingRequest(HttpServletRequest request,
			HttpServletResponse response) 
		DefaultSavedRequest saved = (DefaultSavedRequest) getRequest(request, response);

		if (saved == null) 
			return null;
		

		if (!saved.doesRequestMatch(request, portResolver)) 
			logger.debug("saved request doesn't match");
			return null;
		

		removeRequest(request, response);

		return new SavedRequestAwareWrapper(saved, request);
	

	/**
	 * Allows selective use of saved requests for a subset of requests. By default any
	 * request will be cached by the @code saveRequest method.
	 * <p>
	 * If set, only matching requests will be cached.
	 *
	 * @param requestMatcher a request matching strategy which defines which requests
	 * should be cached.
	 */
	public void setRequestMatcher(RequestMatcher requestMatcher) 
		this.requestMatcher = requestMatcher;
	

	/**
	 * If <code>true</code>, indicates that it is permitted to store the target URL and
	 * exception information in a new <code>HttpSession</code> (the default). In
	 * situations where you do not wish to unnecessarily create <code>HttpSession</code>s
	 * - because the user agent will know the failed URL, such as with BASIC or Digest
	 * authentication - you may wish to set this property to <code>false</code>.
	 */
	public void setCreateSessionAllowed(boolean createSessionAllowed) 
		this.createSessionAllowed = createSessionAllowed;
	

	public void setPortResolver(PortResolver portResolver) 
		this.portResolver = portResolver;
	

可以看出原请求信息时实质上是被缓存到session中了,缓存的是HttpSessionRequestCache实例,任意HttpSessionRequestCache实例均可获得缓存的原请求信息,只要请求的session没有变化。

2)ExceptionTranslationFilter

ExceptionTranslationFilter 是Spring Security的核心filter之一,用来处理AuthenticationException和AccessDeniedException两种异常(由FilterSecurityInterceptor认证请求返回的异常)。

在我们的例子中,AuthenticationException指的是未登录状态下访问受保护资源,AccessDeniedException指的是登陆了但是由于权限不足(比如普通用户访问管理员界面)。

ExceptionTranslationFilter 持有两个处理类,分别是AuthenticationEntryPoint和AccessDeniedHandler。

ExceptionTranslationFilter 对异常的处理是通过这两个处理类实现的,处理规则很简单:

规则1. 如果异常是 AuthenticationException,使用 AuthenticationEntryPoint 处理
规则2. 如果异常是 AccessDeniedException 且用户是匿名用户,使用 AuthenticationEntryPoint 处理
规则3. 如果异常是 AccessDeniedException 且用户不是匿名用户,如果否则交给 AccessDeniedHandler 处理。


对应以下代码

private void handleSpringSecurityException(HttpServletRequest request,
 HttpServletResponse response, FilterChain chain, RuntimeException exception)
 throws IOException, ServletException 
 if (exception instanceof AuthenticationException) 
 logger.debug(
"Authentication exception occurred; redirecting to authentication entry point",
 exception);

 sendStartAuthentication(request, response, chain,
 (AuthenticationException) exception);
 
 else if (exception instanceof AccessDeniedException) 
 if (authenticationTrustResolver.isAnonymous(SecurityContextHolder
 .getContext().getAuthentication())) 
 logger.debug(
 "Access is denied (user is anonymous); redirecting to authentication entry point",
 exception);

 sendStartAuthentication(
 request,
 response,
 chain,
 new InsufficientAuthenticationException(
 "Full authentication is required to access this resource"));
 
 else 
 logger.debug(
 "Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
 exception);

 accessDeniedHandler.handle(request, response,
 (AccessDeniedException) exception);
 
 
 



AccessDeniedHandler 默认实现是 AccessDeniedHandlerImpl。该类对异常的处理是返回403错误码。

public void handle(HttpServletRequest request, HttpServletResponse response,
 AccessDeniedException accessDeniedException) throws IOException,
 ServletException 
 if (!response.isCommitted()) 
 if (errorPage != null)  // 定义了errorPage
 // errorPage中可以操作该异常
request.setAttribute(WebAttributes.ACCESS_DENIED_403,
 accessDeniedException);

 // 设置403状态码
response.setStatus(HttpServletResponse.SC_FORBIDDEN);

 // 转发到errorPage
 RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
 dispatcher.forward(request, response);

 else  // 没有定义errorPage,则返回403状态码(Forbidden),以及错误信息
response.sendError(HttpServletResponse.SC_FORBIDDEN,
 accessDeniedException.getMessage());
 
 


AuthenticationEntryPoint 如果不配置<http>标签的entry-point-ref属性,则默认实现是 LoginUrlAuthenticationEntryPoint, 如果配置了entry-point-ref则用配置的。

protected void sendStartAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain,
			AuthenticationException reason) throws ServletException, IOException 
		// SEC-112: Clear the SecurityContextHolder's Authentication, as the
		// existing Authentication is no longer considered valid
		SecurityContextHolder.getContext().setAuthentication(null);
		requestCache.saveRequest(request, response);                   //缓存原请求
		logger.debug("Calling Authentication entry point.");
		authenticationEntryPoint.commence(request, response, reason);
	

LoginUflAuthenticationEntryPoint该类的处理是转发或重定向到登录页面

public void commence(HttpServletRequest request, HttpServletResponse response,
 AuthenticationException authException) throws IOException, ServletException 

 String redirectUrl = null;

 if (useForward) 

 if (forceHttps && "http".equals(request.getScheme())) 
 // First redirect the current request to HTTPS.
 // When that request is received, the forward to the login page will be
 // used.
 redirectUrl = buildHttpsRedirectUrlForRequest(request);
 

 if (redirectUrl == null) 
 String loginForm = determineUrlToUseForThisRequest(request, response,
 authException);

 if (logger.isDebugEnabled()) 
logger.debug("Server side forward to: " + loginForm);
 

 RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);

 // 转发
dispatcher.forward(request, response);

 return;
 
 
 else 
 // redirect to login page. Use https if forceHttps true

 redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);

 

 // 重定向
redirectStrategy.sendRedirect(request, response, redirectUrl);




了解完这些,回到我们的例子。

第3步时,用户未登录的情况下访问受保护资源,ExceptionTranslationFilter会捕获到AuthenticationException异常(规则1)。页面需要跳转,ExceptionTranslationFilter在跳转前使用requestCache缓存request。

protected void sendStartAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
 AuthenticationException reason) throws ServletException, IOException 
 // SEC-112: Clear the SecurityContextHolder's Authentication, as the
 // existing Authentication is no longer considered valid
 SecurityContextHolder.getContext().setAuthentication(null);
 // 缓存 request
requestCache.saveRequest(request, response);
 logger.debug("Calling Authentication entry point.");
 authenticationEntryPoint.commence(request, response, reason);


requestCache使用的是HttpSessionRequestCache

/**
	 * Stores the current request, provided the configuration properties allow it.
	 */
	public void saveRequest(HttpServletRequest request, HttpServletResponse response) 
		if (requestMatcher.matches(request)) 
			DefaultSavedRequest savedRequest = new DefaultSavedRequest(request,
					portResolver);

			if (createSessionAllowed || request.getSession(false) != null) 
				// Store the HTTP request itself. Used by
				// AbstractAuthenticationProcessingFilter
				// for redirection after successful authentication (SEC-29)
				request.getSession().setAttribute(SAVED_REQUEST, savedRequest);
				logger.debug("DefaultSavedRequest added to Session: " + savedRequest);
			
		
		else 
			logger.debug("Request not saved as configured RequestMatcher did not match");
		
	
总结,在跳转前进行缓存,是缓存到session中。

四、了解了以上原理以及forward和redirect的区别forward和redirect的区别,配置实现如下,基于springsecurity4.1.3版本


配置文件:完整的

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">

<http auto-config="true" use-expressions="true" entry-point-ref="myLoginUrlAuthenticationEntryPoint">
<form-login 
login-page="/login"
authentication-failure-url="/login?error" 
login-processing-url="/login"
authentication-success-handler-ref="myAuthenticationSuccessHandler" /> 
<!-- 认证成功用自定义类myAuthenticationSuccessHandler处理 -->

<logout logout-url="/logout" 
logout-success-url="/" 
invalidate-session="true"
delete-cookies="JSESSIONID"/>

<csrf disabled="true" />
<intercept-url pattern="/order/*" access="hasRole('ROLE_USER')"/>
</http>

<!-- 使用自定义类myUserDetailsService从数据库获取用户信息 -->
<authentication-manager> 
<authentication-provider user-service-ref="myUserDetailsService"> 
<!-- 加密 -->
<password-encoder hash="md5">
</password-encoder>
</authentication-provider>
</authentication-manager>

<!-- 被认证请求向登录界面跳转采用forward方式 -->
<beans:bean id="myLoginUrlAuthenticationEntryPoint" 
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg name="loginFormUrl" value="/login"></beans:constructor-arg>
<beans:property name="useForward" value="true"/>
</beans:bean>

</beans:beans>


1)向登录界面跳转:主要配置
<http auto-config="true" use-expressions="true" entry-point-ref="myLoginUrlAuthenticationEntryPoint">

<!-- 被认证请求向登录界面跳转采用forward方式 -->
<beans:bean id="myLoginUrlAuthenticationEntryPoint" 
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg name="loginFormUrl" value="/login"></beans:constructor-arg>
<beans:property name="useForward" value="true"/>
</beans:bean>
从上面的分析可知,默认情况下采用的是redirect方式,这里通过配置从而实现了forward方式,这里还是直接利用的security自带的类LoginUrlAuthenticationEntryPoint(当然也可以用户自定义了类,下一篇说明如何根据自定义了类实现向不同登录页面的跳转),只不过进行了以上配置:
/**
* Performs the redirect (or forward) to the login form URL.
*/
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException 

String redirectUrl = null;

if (useForward) 

if (forceHttps && "http".equals(request.getScheme())) 
// First redirect the current request to HTTPS.
// When that request is received, the forward to the login page will be
// used.
redirectUrl = buildHttpsRedirectUrlForRequest(request);


if (redirectUrl == null) 
String loginForm = determineUrlToUseForThisRequest(request, response,
authException);

if (logger.isDebugEnabled()) 
logger.debug("Server side forward to: " + loginForm);


RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);

dispatcher.forward(request, response);

return;


else 
// redirect to login page. Use https if forceHttps true

redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);



redirectStrategy.sendRedirect(request, response, redirectUrl);



2)登录信息提交后认证流程

跳转到登录界面,提交登录信息后,经过过滤器UsernamePasswordAuthenticationFilter,该过滤器继承了AbstractAuthenticationProcessingFilter,

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException 

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		if (!requiresAuthentication(request, response)) 
			chain.doFilter(request, response);

			return;
		

		if (logger.isDebugEnabled()) 
			logger.debug("Request is to process authentication");
		

		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);
		
		catch (InternalAuthenticationServiceException failed) 
			logger.error(
					"An internal error occurred while trying to authenticate the user.",
					failed);
			unsuccessfulAuthentication(request, response, failed);

			return;
		
		catch (AuthenticationException failed) 
			// Authentication failed
			unsuccessfulAuthentication(request, response, failed);

			return;
		

		// Authentication success
		if (continueChainBeforeSuccessfulAuthentication) 
			chain.doFilter(request, response);
		

		successfulAuthentication(request, response, chain, authResult);   //认证成功后调用
	
在以上程序中包含了从数据源中获取用户信息并和用户填写的信息进行对比的过程,功能实现attemptAuthentication,这里不对其进行详细分析。

判断是否认证成功,认证成功后执行如下代码:

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);
	

其中successHandler就是配置的MyAuthenticationSuccessHandler。
登录成功后的类配置,存入登录user信息后交给认证成功后的处理类MyAuthenticationSuccessHandler,该类继承了SavedRequestAwareAuthenticationSuccessHandler,他会从缓存中提取请求,从而可以恢复之前请求的数据。初次之外还可以通过配置自定义类实现认证成功后根据权限跳转到不同的页面,例如用户中心和后台管理中心,下一篇会详细说明。

/**
* 登录后操作
* 
* @author HHL
* @date
* 
*/
@Component
public class MyAuthenticationSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler 

@Autowired
private IUserService userService;

@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException 

// 认证成功后,获取用户信息并添加到session中
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
MangoUser user = userService.getUserByName(userDetails.getUsername());
request.getSession().setAttribute("user", user);

super.onAuthenticationSuccess(request, response, authentication);






SavedRequestAwareAuthenticationSuccessHandler中的onAuthenticationSuccess方法;

@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException 
SavedRequest savedRequest = requestCache.getRequest(request, response);

if (savedRequest == null) 
super.onAuthenticationSuccess(request, response, authentication);

return;

String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request
.getParameter(targetUrlParameter)))) 
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);

return;


clearAuthenticationAttributes(request);

// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);


4.1.3中如果配置了 authentication-success-handler-ref,则首先使用该配置的,如果配置了authentication-success-forward-url,则使用该配置的,如果都没有配置则采用的SavedRequestAwareAuthenticationSuccessHandler进行处理,详情可参见: Spring实战篇系列----源码解析Spring Security中的过滤器Filter初始化


上述实现了跳转到登录界面采用forward方式,就是浏览器地址栏没有变化,当然也可采用redirect方式,地址栏变为登录界面地址栏,当登录完成后恢复到原先的请求页面,请求信息会从requestCache中还原回来。可参考 Spring实战篇系列----spring security4.1.3配置以及踩过的坑

五、附上信息恢复的调用栈

WebUtils.getParametersStartingWith(ServletRequest, String) line: 663	
ServletRequestParameterPropertyValues.<init>(ServletRequest, String, String) line: 77	
ServletRequestParameterPropertyValues.<init>(ServletRequest) line: 52	
ExtendedServletRequestDataBinder(ServletRequestDataBinder).bind(ServletRequest) line: 100	
ServletModelAttributeMethodProcessor.bindRequestParameters(WebDataBinder, NativeWebRequest) line: 150	
ServletModelAttributeMethodProcessor(ModelAttributeMethodProcessor).resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory) line: 114	
HandlerMethodArgumentResolverComposite.resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory) line: 121	
ServletInvocableHandlerMethod(InvocableHandlerMethod).getMethodArgumentValues(NativeWebRequest, ModelAndViewContainer, Object...) line: 161	
ServletInvocableHandlerMethod(InvocableHandlerMethod).invokeForRequest(NativeWebRequest, ModelAndViewContainer, Object...) line: 128	
ServletInvocableHandlerMethod.invokeAndHandle(ServletWebRequest, ModelAndViewContainer, Object...) line: 114	
RequestMappingHandlerAdapter.invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 827	
RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 738	
RequestMappingHandlerAdapter(AbstractHandlerMethodAdapter).handle(HttpServletRequest, HttpServletResponse, Object) line: 85	
DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse) line: 963	
DispatcherServlet.doService(HttpServletRequest, HttpServletResponse) line: 897	
DispatcherServlet(FrameworkServlet).processRequest(HttpServletRequest, HttpServletResponse) line: 970	
DispatcherServlet(FrameworkServlet).doPost(HttpServletRequest, HttpServletResponse) line: 872	
DispatcherServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 647	
DispatcherServlet(FrameworkServlet).service(HttpServletRequest, HttpServletResponse) line: 846	
DispatcherServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 728	
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 303	
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 208	
WsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 91	
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 241	
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 208	
SiteMeshFilter(ContentBufferingFilter).bufferAndPostProcess(FilterChain, HttpServletRequest, HttpServletResponse, Selector) line: 169	
SiteMeshFilter(ContentBufferingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 126	
SiteMeshFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 120	
ConfigurableSiteMeshFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 163	
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 241	
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 208	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 317	
FilterSecurityInterceptor.invoke(FilterInvocation) line: 127	
FilterSecurityInterceptor.doFilter(ServletRequest, ServletResponse, FilterChain) line: 91	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
ExceptionTranslationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 115	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
SessionManagementFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 137	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
AnonymousAuthenticationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 111	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
SecurityContextHolderAwareRequestFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 169	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
RequestCacheAwareFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 63	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
BasicAuthenticationFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 158	
BasicAuthenticationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 200	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
LogoutFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 121	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
HeaderWriterFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 66	
HeaderWriterFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
WebAsyncManagerIntegrationFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 56	
WebAsyncManagerIntegrationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
SecurityContextPersistenceFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 105	
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331	
FilterChainProxy.doFilterInternal(ServletRequest, ServletResponse, FilterChain) line: 214	
FilterChainProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 177	
DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain) line: 346	
DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 262	
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 241	
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 208	
CharacterEncodingFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 197	
CharacterEncodingFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107	
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 241	
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 208	
StandardWrapperValve.invoke(Request, Response) line: 223	
StandardContextValve.invoke(Request, Response) line: 107	
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 504	
StandardHostValve.invoke(Request, Response) line: 155	
ErrorReportValve.invoke(Request, Response) line: 75	
AccessLogValve.invoke(Request, Response) line: 934	
StandardEngineValve.invoke(Request, Response) line: 90	
CoyoteAdapter.service(Request, Response) line: 494	
Http11AprProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1009	
Http11AprProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 632	
Http11AprProtocol$Http11ConnectionHandler.process(SocketWrapper<Long>, SocketStatus) line: 281	
AprEndpoint$SocketProcessor.doRun() line: 2248	
AprEndpoint$SocketProcessor.run() line: 2237	
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: not available	
ThreadPoolExecutor$Worker.run() line: not available	
TaskThread(Thread).run() line: not available	



总结:

1)被认证请求被FilterSecurityInterceptor拦截看有没有对应权限,如果没有抛异常给ExceptionTranslationFilter 2)ExceptionTranslationFilter缓存原请求,利用LoginUrlAuthenticationEntryPoint入口跳转到登录界面 3)用户在登录界面填写登录信息后,提交,经过UsernamePasswordAuthenticationFilter对填写的信息和从数据源中获取的信息进行对比,成功则授权权限,并通过登录成功后入口SavedRequestAwareAuthenticationSuccessHandler跳转回原请求页面(跳转时有从缓存中对请求信息的恢复) 4)登录完成后返回原请求,由FilterSecurityInterceptor进行权限的验证(大部分工作有AbstractSecurityInterceptor来做),根据登录成功后生成的Authentication(Authentication authentication = SecurityContextHolder.getContext().getAuthentication();由SecurityContextHolder持有,而其中的SecurityContext由 SecurityContextPersistentFilter保存到session中从而实现request共享 )中的权限和请求所需的权限对比,如果一致则成功执行,如果权限不正确则返回403错误码 5)以上均是默认情况下,没有经过配置的执行过程,当然可以自定义LoginUrlAuthenticationEntryPoint和SavedRequestAwareAuthenticationSuccessHandler实现根据不同的请求所需权限跳转到不同登录页面及授权成功后根据权限跳转到不同页面,以及返回403错误码时跳转到对应的页面(AccessDeniedHandlerImpl)在下一篇中会对其进行实现     

spring实战----security4.1.3认证的过程以及原请求信息的缓存及恢复(requestcache)(代码片段)

一、先看下认证过程认证过程分为7步:1.用户访问网站,打开了一个链接(originurl)。2.请求发送给服务器,服务器判断用户请求了受保护的资源。3.由于用户没有登录,服务器重定向到登录页面4.填写表单,点击登录5... 查看详情

spring实战----security4.1.3实现根据请求跳转不同登录页以及登录后根据权限跳转到不同页配置(代码片段)

一、背景介绍上一篇最后总结说了:1)被认证请求被FilterSecurityInterceptor拦截看有没有对应权限,如果没有抛异常给ExceptionTranslationFilter2)ExceptionTranslationFilter缓存原请求,利用LoginUrlAuthenticationEntryPoint入口跳 查看详情

spring实战----开篇(包含系列目录链接)

终于还是要对Spring进行解剖,接下来Spring实战篇系列会以应用了Spring技术的JavaWeb的应用mango为例,来分析Spring各个模块的技术,包括源码解析等,谨以此记!!! 【Spring实战】----开发环境配置【Spring... 查看详情

spring实战笔记:spring核心

springcore一.简介: 1.依赖注入(DI)   优点:解耦   Spring通过应用上下文(ApplicationContext)装载bean的定义,并把它们组装起来。   Spring应用上下文负责对象的创建和组装。    &n... 查看详情

spring实战

Spring的两个核心特性:依赖注入(DI)和面向切面编程(AOP) 为了降低Java开发的复杂性,Spring采取了以下4种关键策略:  基于POJO的轻量级和最小侵入性编程  通过依赖注入和面向接口实现松耦合  基于切面和惯例进行声明... 查看详情

spring4.1.8扩展实战之四:感知spring容器变化(smartlifecycle接口)(代码片段)

欢迎访问我的GitHub本篇概览本章是《spring4.1.8扩展实战》的第四篇,如果业务上需要在spring容器启动和关闭的时候做一些操作,可以自定义SmartLifecycle接口的实现类来扩展,本章我们通过先分析再实战的方法,来掌握这种扩展方... 查看详情

spring实战笔记:spring集成

...是客户端应用和服务端之间的会话。    1.Spring通过多种远程调用技术支持RPC(remoteprocedurecall,远程过程调用)RPC模型使用场景RMI不考虑网络限制时(例如防火墙),访问/发布基于Java的服务Hessian或Burlap考虑网络限... 查看详情

[dubbo实战]dubbo+zookeeper+spring实战(转)

     这里最熟悉的就是spring了,项目中应用很多。dubbo是一个实现分布式的框架,zookeeper是注册中心。给我的感觉就像多对多关系的两者表,zookeeper相当于第三张表维护关系。下面通过一个小程序加深认识。一... 查看详情

《spring实战(第三版)》之旅

...糊。  反正经过这五大框架的学习,感觉最重要的还是Spring框架,于是乎,开学时期偶得一书《Spring实战》,在网上口碑还是蛮高的,于是继续深入学习(复习)下Spring这个框架。  到目前为止已经读到了第8章使用SpringWebFl... 查看详情

spring实战入门,带你轻松掌握spring框架

Spring框架是什么?Spring是于2003年兴起的一个轻量级的Java开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring的核心是控制反转(IoC)和面向切面编程(AOP)。Spring是可以在JavaSE/EE中使用的轻量级开源框架。Spring的主... 查看详情

spring4实战学习笔记

《Spring4实战第4版》2016年4月新出版的,之前的第三版看起来还是不错的,所以看到新版就直接买下来。英文版源码地址:SpringinAction,FourthEditionCoversSpring4 1.IOC装配Bean参考【Spring实战4 2.2】,作者提倡无XML配置化... 查看详情

spring实战笔记二

一、Spring配置的方式    spring容器用来创建应用程序中的bean,并通过DI来协调这些对象间的关系。    在bean进行装配时,spring提供了三种主要的装配机制。      ①在XML中进行显式配置      ②在Java中进... 查看详情

spring实战-手动创建spring项目结构

环境:MacOS+IntelliJIDEA2019.3.1(UltimateEdition)1、创建存放项目的文件夹2、创建pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:x 查看详情

spring实战--1spring的核心

  最近面试总会涉及Spring的优点,SpringMVC与Struts2的比较,生活慢慢稳定下来,这些面试还是应了那句话“只顾盲目拉车,不会低头看路”,回过头来还是要好好研究一下Spring,如果仅仅是停留在粘贴拷贝像从前一样机械... 查看详情

spring07_纯注解实战及spring整合junit

本教程源码请访问:tutorial_demo在《Spring04_IOC和DI实战》当中,我们使用XML配置的方式结合ApacheCommonsDbUtils实现单表的CRUD操作,这篇教程我们使用纯注解方式结合ApacheCommonsDbUtils实现单表的CRUD操作。一、需求和技术要求1.1、需求实... 查看详情

spring实战笔记:web中的spring

一.构建SpringWeb应用1.SpringMVC中用户请求处理  上图展示了请求使用SpringMVC所经历的所有站点。  1:在请求离开浏览器时,会带有所有请求内容的信息,至少会包含请求的URL。     请求通过Spring的Di... 查看详情

spring实战-第二章-装配bean

Bean是Spring对象的声明(装配wiring),要使用Spring,必须先装配需要使用的对象,有3种装配的方式自动化装配Bean通过Java代码装配通过XML装配自动化装配Bean自动化装配Bean很简单1.声明接口packagesoundsystem;publicinterfaceCompactDisc{voidplay(... 查看详情

spring实战spring注解配置工作原理源码解析(代码片段)

一、背景知识在【Spring实战】Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题、目标去看,这样才能最... 查看详情