首页 > 技术文章 > Springboot security cas源码陶冶-ExceptionTranslationFilter

question-sky 2017-06-22 17:18 原文

拦截关键的两个异常,对异常进行处理。主要应用异常则跳转至cas服务端登录页面

ExceptionTranslationFilter#doFilter-逻辑入口

具体操作逻辑如下

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		//这里直接放行,看出来其主要处理的是异常发生的情况
		try {
			chain.doFilter(request, response);

			logger.debug("Chain processed normally");
		}
		//IO异常直接抛出
		catch (IOException ex) {
			throw ex;
		}
		catch (Exception ex) {
			//提取发生的异常
			Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
			//首先获取是否含有AuthenticationException异常
			RuntimeException ase = (AuthenticationException) throwableAnalyzer
					.getFirstThrowableOfType(AuthenticationException.class, causeChain);

			if (ase == null) {
				//没有AuthenticationException异常则尝试获取AccessDeniedException异常
				ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
						AccessDeniedException.class, causeChain);
			}

			if (ase != null) {
				//处理指定的这两个关键异常
				handleSpringSecurityException(request, response, chain, ase);
			}
			else {
				//如果关键异常都没有则直接抛出
				if (ex instanceof ServletException) {
					throw (ServletException) ex;
				}
				else if (ex instanceof RuntimeException) {
					throw (RuntimeException) ex;
				}
				throw new RuntimeException(ex);
			}
		}
	}

由以上代码我们只需要着重分析下handleSpringSecurityException()方法即可

ExceptionTranslationFilter#handleSpringSecurityException-处理spring安全异常

源码如下

	private void handleSpringSecurityException(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain, RuntimeException exception)
			throws IOException, ServletException {
		if (exception instanceof AuthenticationException) {
			//转发请求至cas服务端登录页面
			sendStartAuthentication(request, response, chain,
					(AuthenticationException) exception);
		}
		else if (exception instanceof AccessDeniedException) {
		//首先判断是否Authentication凭证是否为AnonymousAuthenticationToken
			if (authenticationTrustResolver.isAnonymous(SecurityContextHolder
					.getContext().getAuthentication())) {
				//对AnonymousAuthenticationToken类型的请求转发cas服务端登录请求页面
				sendStartAuthentication(
						request,
						response,
						chain,
						new InsufficientAuthenticationException(
								"Full authentication is required to access this resource"));
			}
			else {
				//对于非AnonymousAuthenticationToken类型的请求,比如UsernamePasswordAuthenticationToken/CasAuthenticationToken则直接将异常信息写出到页面
				accessDeniedHandler.handle(request, response,
						(AccessDeniedException) exception);
			}
		}
	}

紧接着我们看ExceptionTranslationFilter#sendStartAuthentication()方法

	protected void sendStartAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain,
			AuthenticationException reason) throws ServletException, IOException {
		//转发前先清楚凭证信息
		SecurityContextHolder.getContext().setAuthentication(null);
		requestCache.saveRequest(request, response);
		
		//通过AuthenticationEntryPoint对象转发请求,常见为CasAuthenticationEntryPoint
		authenticationEntryPoint.commence(request, response, reason);
	}

CasAuthenticationEntryPoint#commence-cas服务端登录请求转发

直接查看源码,代码很简单

	public final void commence(final HttpServletRequest servletRequest,
			final HttpServletResponse response,
			final AuthenticationException authenticationException) throws IOException,
			ServletException {
		//创建service回调参数路径
		final String urlEncodedService = createServiceUrl(servletRequest, response);
		//cas服务端登录请求路径拼装,即后面会添加service参数
		final String redirectUrl = createRedirectUrl(urlEncodedService);

		//模板方法,供子类复写
		preCommence(servletRequest, response);
		//直接转发
		response.sendRedirect(redirectUrl);
	}

CasAuthenticationEntryPoint以下属性必须配置

  1. loginUrl-cas服务端的登录地址,比如https://www.examplecasserver.com/cas/login
  2. ServiceProperties-回调路径的配置,主要是service属性,此处一般为应用的主页地址

小结

  1. ExceptionTranslationFilter的执行逻辑比较简单,主要是接受AccessDeniedExceptionAuthenticationException两大请求,并最终转发至cas服务端登录界面

  2. ExceptionTranslationFilter的搭档是FilterSecurityInterceptor,其介绍可查看>>>Springboot security cas源码陶冶-FilterSecurityInterceptor

  3. Note:CasAuthenticationEntryPoint在配置service回调地址时,不可为应用的登录请求地址,不然会提前被CasAuthenticationFilter拦截直接输出错误至页面了

推荐阅读