SpringSecurity學習筆記二

SpringSecurity認證成功、失敗處理器原理分析

首先需要知道的是,SpringSecurity默認的成功、失敗處理器是SavedRequestAwareAuthenticationSuccessHandlerSimpleUrlAuthenticationFailureHandler;正常情況下,請求一個資源,在進入到SpringSecurity的認證中,認證成功纔會跳轉到待請求的資源

SavedRequestAwareAuthenticationSuccessHandler源碼分析

認證成功處理器流程:用戶請求一個接口時,會被SpringSecurity攔截。當用戶認證成功後,纔會跳轉到之前用戶想要請求的接口那裏

問題:怎麼知道用戶的請求呢?
SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response)。這個方法就可以將用戶的請求緩存起來。

假設此時用戶已經認證成功,那麼關於認證成功處理器的調用流程是:
AbstractAuthenticationProcessingFilter#successfulAuthentication>AuthenticationSuccessHandler#onAuthenticationSuccess>SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess

SavedRequestAwareAuthenticationSuccessHandler

@Override
//這裏是重寫了該方法,與父類裏的實現不一樣了
	public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication)
			throws ServletException, IOException {
			//將用戶的原始請求緩存住 放到SavedRequest裏
		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);

		String targetUrl = savedRequest.getRedirectUrl();
		logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
		//跳轉到指定的原用戶請求路徑
		getRedirectStrategy().sendRedirect(request, response, targetUrl);
	}

這裏的getRedirectStrategy()是DefaultRedirectStrategy。

DefaultRedirectStrategy

public void sendRedirect(HttpServletRequest request, HttpServletResponse response,
			String url) throws IOException {
			//處理url
		String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
		redirectUrl = response.encodeRedirectURL(redirectUrl);

		if (logger.isDebugEnabled()) {
			logger.debug("Redirecting to '" + redirectUrl + "'");
		}
		//這裏執行跳轉操作
		response.sendRedirect(redirectUrl);
	}

SavedRequestAwareAuthenticationSuccessHandler源碼分析

onAuthenticationFailure方法被調用時,AuthenticationFailureHandler會根據defaultFailureUrl的路徑進行跳轉。如果defaultFailureUrl沒有設置,那麼會發送401錯誤碼和與AuthenticationException有關的錯誤信息給客戶端。
如果useForward屬性被設置,那麼RequestDispatcher.forware將會重定向到目的地而不是跳轉。

public void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {
		//如果默認的錯誤跳轉路徑爲空
		if (defaultFailureUrl == null) {
			logger.debug("No failure URL set, sending 401 Unauthorized error");

			response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
					"Authentication Failed: " + exception.getMessage());
		}
		else {
			//保存異常信息
			saveException(request, exception);
			//如果forwardToDestination屬性值爲true
			if (forwardToDestination) {
				logger.debug("Forwarding to " + defaultFailureUrl);

				request.getRequestDispatcher(defaultFailureUrl)
						.forward(request, response);
			}
			else {
				logger.debug("Redirecting to " + defaultFailureUrl);
				redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
			}
		}
	}
	
	//根據forwardToDestination的值 選擇性的將錯誤信息 保存在request[重定向與原請求公用一個request域]中或session中。
	protected final void saveException(HttpServletRequest request,
			AuthenticationException exception) {
		if (forwardToDestination) {
			request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
		}
		else {
			HttpSession session = request.getSession(false);

			if (session != null || allowSessionCreation) {
				request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,
						exception);
			}
		}
	}

自定義成功、失敗處理器

成功處理器

@Component
@Log
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SecurityProperties securityProperties;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登錄成功處理器");
        	//這種方式 即調用父類的方法 是成功之後跳轉到指定url
          //  super.onAuthenticationSuccess(request, response, authentication);
       
       //下面這種方式 是將認證成功的用戶信息打印到控制檯
          response.setContentType("application/json;charset=UTF-8");
          response.getWriter().write(objectMapper.writeValueAsString(authentication));
        }
    }
}

失敗處理器

@Component
@Log
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    //SpringMVC加載JSON解析工具時 注入的 可以直接用
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SecurityProperties securityProperties;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登錄失敗處理器");
       //這種方式 即調用父類的方法 是失敗之後跳轉
           // super.onAuthenticationFailure(request, response, exception);
         //下面這種方式 是將認證失敗的錯誤信息打印到控制檯
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(exception));
        }
    }
}

將自定義認證處理器加入到SpringSecurity中

@Configuration
@EnableWebSecurity
public class WebMvcSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationFailureHandler failureHandler;

    @Autowired
    private MyAuthenticationSuccessHandler successHandler;


    @Override
    protected void configure(HttpSecurity http) throws Exception {

         http.formLogin()
         .successHandler(successHandler)
         .failureHandler(failureHandler)
         .and().authorizeRequests().anyRequest().authenticated();

    }

}

這樣認證過程中 就能走到我們自己定義的認證處理器了。

以上,如有錯誤,請指出謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章