Springboot security cas源码陶冶-ExceptionTranslationFilter

时间:2022-12-14 21:04:33

拦截关键的两个异常,对异常进行处理。主要应用异常则跳转至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拦截直接输出错误至页面了