• Spring Security SavedRequestAwareAuthenticationSuccessHandler类


    SavedRequestAwareAuthenticationSuccessHandler类是SpringSecurity提供的登录成功处理器,登录成功后该处理器会从Session中获取认证之前访问的url,然后将用户重定向到该url地址,

    1. 设置认证之前的url

      RequestCache.java

      	/**
      	 * Caches the current request for later retrieval, once authentication has taken
      	 * place. Used by <tt>ExceptionTranslationFilter</tt>.
      	 * @param request the request to be stored
      	 */
      	void saveRequest(HttpServletRequest request, HttpServletResponse response);
      
      	/**
      	 * Returns the saved request, leaving it cached.
      	 * @param request the current request
      	 * @return the saved request which was previously cached, or null if there is none.
      	 */
      	SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
      
      	/**
      	 * Returns a wrapper around the saved request, if it matches the current request. The
      	 * saved request should be removed from the cache.
      	 * @param request
      	 * @param response
      	 * @return the wrapped save request, if it matches the original, or null if there is
      	 * no cached request or it doesn't match.
      	 */
      	HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response);
      
      	/**
      	 * Removes the cached request.
      	 * @param request the current request, allowing access to the cache.
      	 */
      	void removeRequest(HttpServletRequest request, HttpServletResponse response);
      

      在spring security中RequestCache有三个默认实现,HttpSessionRequestCache通过session存储前一次请求信息、NullRequestCache一般禁用session的情况下使用,不会存储前一次请求信息、CookieRequestCache使用cookie存储前一次请求信息。

      ExceptionTranslationFilter.java

      private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      			throws IOException, ServletException {
      		try {
      			chain.doFilter(request, response);
      		}
      		catch (IOException ex) {
      			throw ex;
      		}
      		catch (Exception ex) {
      			// Try to extract a SpringSecurityException from the stacktrace
      			Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
      			RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
      					.getFirstThrowableOfType(AuthenticationException.class, causeChain);
      			if (securityException == null) {
      				securityException = (AccessDeniedException) this.throwableAnalyzer
      						.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
      			}
      			if (securityException == null) {
      				rethrow(ex);
      			}
      			if (response.isCommitted()) {
      				throw new ServletException("Unable to handle the Spring Security Exception "
      						+ "because the response is already committed.", ex);
      			}
      			handleSpringSecurityException(request, response, chain, securityException);
      		}
      	}
      
      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
      		SecurityContext context = SecurityContextHolder.createEmptyContext();
      		SecurityContextHolder.setContext(context);
      		this.requestCache.saveRequest(request, response);
      		this.authenticationEntryPoint.commence(request, response, reason);
      	}
      

      ExceptionTranslationFilter在捕获身份认证异常之后会调用requestCache将此次请求存储,以便登录成功后由SavedRequestAwareAuthenticationSuccessHandler调用此次请求信息,并重定向到url。

    2. 重定向到认证前url

      SavedRequestAwareAuthenticationSuccessHandler.java

      private RequestCache requestCache = new HttpSessionRequestCache();
      
      	@Override
      	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
      			Authentication authentication) throws ServletException, IOException {
      		SavedRequest savedRequest = this.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)))) {
      			this.requestCache.removeRequest(request, response);
      			super.onAuthenticationSuccess(request, response, authentication);
      			return;
      		}
      		clearAuthenticationAttributes(request);
      		// Use the DefaultSavedRequest URL
      		String targetUrl = savedRequest.getRedirectUrl();
      		getRedirectStrategy().sendRedirect(request, response, targetUrl);
      	}
      

      在SavedRequestAwareAuthenticationSuccessHandler源码中有一个RequestCache属性,在登录成功后会调用RequestCache.getRequest获取前一次请求信息然后重定向到该url。

    3. 自定义RequestCache

      public class RedisRequestCache 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 RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
      
          private String sessionAttrName = SAVED_REQUEST;
      
          @Autowired
          private RedisCache redisCache;
      
          /**
           * Stores the current request, provided the configuration properties allow it.
           */
          @Override
          public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
              if (!this.requestMatcher.matches(request)) {
                  if (this.logger.isTraceEnabled()) {
                      this.logger.trace(
                              LogMessage.format("Did not save request since it did not match [%s]", this.requestMatcher));
                  }
                  return;
              }
              String redirectUrl = UrlUtils.buildFullRequestUrl(request);
              // DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, this.portResolver);
              redisCache.setCacheObject(this.sessionAttrName, encodeCookie(redirectUrl));
          }
      
          @Override
          public SavedRequest getRequest(HttpServletRequest currentRequest, HttpServletResponse response) {
              String originalURI = redisCache.getCacheObject(this.sessionAttrName);
              if (StringUtils.isEmpty(originalURI)) {
                  return null;
              }
              // var str = JSON.toJSONString(originalURI);
              UriComponents uriComponents = UriComponentsBuilder.fromUriString(decodeCookie(originalURI)).build();
              DefaultSavedRequest.Builder builder = new DefaultSavedRequest.Builder();
              int port = getPort(uriComponents);
              return builder.setScheme(uriComponents.getScheme()).setServerName(uriComponents.getHost())
                      .setRequestURI(uriComponents.getPath()).setQueryString(uriComponents.getQuery()).setServerPort(port)
                      .setMethod(currentRequest.getMethod()).build();
              // return JSON.parseObject(str, SavedRequest.class);
              // return (SavedRequest) savedRequest;
          }
      
          @Override
          public void removeRequest(HttpServletRequest currentRequest, HttpServletResponse response) {
              redisCache.deleteObject(this.sessionAttrName);
          }
      
          @Override
          public HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response) {
              SavedRequest saved = getRequest(request, response);
              if (saved == null) {
                  this.logger.trace("No saved request");
                  return null;
              }
              if (!matchesSavedRequest(request, saved)) {
                  if (this.logger.isTraceEnabled()) {
                      this.logger.trace(LogMessage.format("Did not match request %s to the saved one %s",
                              UrlUtils.buildRequestUrl(request), saved));
                  }
                  return null;
              }
              removeRequest(request, response);
              if (this.logger.isDebugEnabled()) {
                  this.logger.debug(LogMessage.format("Loaded matching saved request %s", saved.getRedirectUrl()));
              }
              return request;
          }
      
          private boolean matchesSavedRequest(HttpServletRequest request, SavedRequest savedRequest) {
              if (savedRequest instanceof DefaultSavedRequest) {
                  DefaultSavedRequest defaultSavedRequest = (DefaultSavedRequest) savedRequest;
                  return defaultSavedRequest.doesRequestMatch(request, this.portResolver);
              }
              String currentUrl = UrlUtils.buildFullRequestUrl(request);
              return savedRequest.getRedirectUrl().equals(currentUrl);
          }
      
          private int getPort(UriComponents uriComponents) {
              int port = uriComponents.getPort();
              if (port != -1) {
                  return port;
              }
              if ("https".equalsIgnoreCase(uriComponents.getScheme())) {
                  return 443;
              }
              return 80;
          }
      
          private static String encodeCookie(String cookieValue) {
              return Base64.getEncoder().encodeToString(cookieValue.getBytes());
          }
      
          private static String decodeCookie(String encodedCookieValue) {
              return new String(Base64.getDecoder().decode(encodedCookieValue.getBytes()));
          }
      
          public void setRequestMatcher(RequestMatcher requestMatcher) {
              this.requestMatcher = requestMatcher;
          }
      
          public void setPortResolver(PortResolver portResolver) {
              this.portResolver = portResolver;
          }
      
          public void setSessionAttrName(String sessionAttrName) {
              this.sessionAttrName = sessionAttrName;
          }
      
      }
      
      @Override
      public void configure(HttpSecurity http) throws Exception {
        http.requestCache(httpSecurityRequestCacheConfigurer -> {
          httpSecurityRequestCacheConfigurer.requestCache(redisRequestCache());
        });
      }
      

      自定义登录成功处理器需要手动配置自定义的RequestCache。

  • 相关阅读:
    luoguP2939 [USACO09FEB]改造路Revamping Trails
    出题
    数字游戏
    统一异常处理
    数据验证
    拦截器
    数据绑定和表单标签库
    类型转换和格式化
    Spring MVC入门
    4springboot:日志(下)
  • 原文地址:https://www.cnblogs.com/yourblog/p/16322589.html
Copyright © 2020-2023  润新知