`

spring-security(十九)核心Filter-ExceptionTranslationFilter

阅读更多
前言:
  在spring的安全过滤器链中ExceptionTranslationFilter在FilterSecurityInterceptor的前面,这个过滤器自身并不执行具体的安全防护,他主要处理FilterSecurityInterceptor这个过滤器抛出的各种异常,并返回给客户端一个合适的http响应。
一、ExceptionTranslationFilter功能和属性
1.在类中主要有以下几个属性
  • AuthenticationEntryPoint-处理需要重新认证的逻辑
  • AccessDeniedHandler-处理请求被拒绝的逻辑
  • AuthenticationTrustResolver-判断处理类型
  • RequestCache-缓存客户端请求,当认证完成后能继续执行引发认证的原始请求

2.这个过滤器的主要逻辑在handleSpringSecurityException方法中,步骤如下
  • 如果捕获到的异常是AuthenticationException,就重新执行认证
  • 如果捕获到的异常是AccessDeniedException,再进一步执行下面的判断
  •          ■ 如果当前的认证形式是Anonymous或者RememberMe,则重新执行认证
             ■ 否则就是当前认证用户没有权限访问被请求资源,调用accessDeniedHandler.handle方法

下面看下重新认证的方法sendStartAuthentication
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);
}

首先会将现在SecurityContextHolder中的认证对象清除,之后将当前的调用保存在requestCache中,以便后面认证成功后能继续进行当前的处理,接着就调用authenticationEntryPoint的commence方法,将用户重定向到认证界面,提示用户进行认证。
3. AuthenticationEntryPoint类
这个类的主要作用是呈现给用户一个合适的响应从而提示用户能够重新登录,常见的实现类有
  • BasicAuthenticationEntryPoint-基本认证对应的EntryPoint,修改Response的header,返回用户一个401码
  • CasAuthenticationEntryPoint-cas认证对应的EntryPoint,将用户重定向到Cas服务器的登录页面
  • LoginUrlAuthenticationEntryPoint-form login认证对应的EntryPoint,重定向到登录页面
  • DelegatingAuthenticationEntryPoint-当系统中有多个EntryPoint,每个EntryPoint对应各自的匹配路径时采用这个EntryPoint。这个类维护一个RequestMatcher和EntryPoint对应关系的hash表,根据传入的request找到匹配的EntryPoint执行对应的commence方法,这个类里面还有一个defaultEntryPoint,当所有的EntryPoint都不匹配当前请求时调用默认EntryPoint的commence方法。

下面以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);
}

简单说明如下
  • 当配置采用forward形式时,如果配置的scheme必须是https,而当前发起的请求是http时,先发一个重定向,和当前url一样仅仅把http修改成https,这样会重新抛出认证异常,重新进入到这个方法里,此时请求已变成https了
  • 之后获取loginFormUrl,直接forward到这个对应的界面
  • 如果配置的是不采用forward,则构建一个redirect到登录页面的url,之后直接重定向到这个界面

4.AccessDeniedHandler类
这个类主要用来处理当用户进行了认证,当时没有权限方法当前的资源而出现访问拒绝的情况。在正常使用场景下这种状况是不应该发生,因为我们的应用应该只提供能操作的UI给用户,比如一个供admin访问的页面应该对没有admin权限的用户隐藏,但是我们的安全策略不能只是依赖隐藏链接的方式,因为恶意的用户可以直接输入URL或者通过修改RESTful URL的参数等方式来绕过这个限制,在这个场合下就会出现AccessDeniedException异常。
当出现AccessDeniedException异常时,ExceptionTranslationFilter就会委托AccessDeniedHandler来进行处理,默认情况下AccessDeniedHandlerImpl会被调用,向客户端返回一个403码,我们可以通过配置AccessDeniedHandlerImpl的errorpage属性,从而向用户展示一个友好的页面(重新login页面),也可以通过自己实现AccessDeniedHandler提供定制化的服务。
5.SavedRequest和RequestCache类
ExceptionTranslationFilter类的另外一个功能就是在调用AuthenticationEntryPoint之前把当前的请求保存起来,默认利用HttpSessionRequestCache保存在session中,从而在认证完成后能重新执行当前的处理。如UsernamePasswordAuthenticationFilter和CasAuthenticationFilter在认证成功后通过调用SavedRequestAwareAuthenticationSuccessHandler这个类的onAuthenticationSuccess方法里,利用HttpSessionRequestCache从session中获取保存过的请求信息后,获取对应的url,发送一个重定向继续之前请求,RequestCacheAwareFilter类再将当前request换成保存的request从而恢复之前请求的完整信息。
二、.在spring boot环境下,采用Java config机制这个filter是如何追加到servlet中对我们的请求进行拦截的呢
在前面的章节(spring-security(十六)Filter配置原理)中,我们知道spring 安全相关的Filter是在WebSecurity的build方法中调用HttpSecurity的build来将追加到HttpSecurity中filter列表排好序后构建成SecurityFilterChain,再把所有的SecurityFilterChain追加到FilterChainProxy中,最后通过DelegatingFilterProxy注册到ServletContext中的,下面我们主要来看下这个类是如何追加到HttpSecuriy的filter列表中的,以及对应的主要属性是如何配置的。
1. 从我们的配置入口WebSecurityConfigurerAdapter类开始,在这个类的getHttp()方法中,采用默认配置时会调用
http.exceptionHandling()方法,在这个方法中创建了一个实现了SecurityConfigurer接口的配置类ExceptionHandlingConfigurer,通过调用getOrApply方法最终追加到HttpSecurity的configurers属性中,通过这个配置类我们也可以设置ExceptionTranslationFilter中的authenticationEntryPoint、accessDeniedHandler等属性。
2. WebSecurity在构建HttpSecurity时,会调用HttpSecurity的build方法,这个方法会先执行HttpSecurity的configure()方法,就是依次调用configurers属性中各个SecurityConfigurer的configure方法
private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}

private Collection<SecurityConfigurer<O, B>> getConfigurers() {
	List<SecurityConfigurer<O, B>> result = new ArrayList<SecurityConfigurer<O, B>>();
	for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
		result.addAll(configs);
	}
	return result;
}

3. 下面来看下ExceptionHandlingConfigurer的configure方法
public void configure(H http) throws Exception {
	AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
	ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint, getRequestCache(http));
		if (accessDeniedHandler != null) {
			exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
		}
		exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
		http.addFilter(exceptionTranslationFilter);
	}

可以看到,在这个类里面会创建一个Filter,并追加到HttpSecurity的filter列表中。
这样就可以明确看出我们的ExceptionTranslationFilter被加入到了httpsecurity的filter列表中了,下面我们看下这个filter中几个主要属性是怎么设置的
首先是创建entryPoint的getAuthenticationEntryPoint方法
AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
		AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
		if (entryPoint == null) {
			entryPoint = createDefaultEntryPoint(http);
		}
		return entryPoint;
	}

private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
	if (defaultEntryPointMappings.isEmpty()) {
		return new Http403ForbiddenEntryPoint();
	}
	if (defaultEntryPointMappings.size() == 1) {
		return defaultEntryPointMappings.values().iterator().next();
	}
	DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(defaultEntryPointMappings);
	entryPoint.setDefaultEntryPoint(defaultEntryPointMappings.values().iterator().next());
	return entryPoint;
}

因为默认配置下我们没有给this.authenticationEntryPoint赋值,所以会调用createDefaultEntryPoint(H http)来获取,那这个地方的defaultEntryPointMappings是怎么设置的呢,为了说明这个属性,我们需要额外的看一些配置类。
例如当我们启用formLogin功能时,通过http.formLogin()配置,此时会向http的configurers列表中追加一个FormLoginConfigurer类,因为在httpsecurity的build方法中会先执行各个configure的init方法,我们下面看下FormLoginConfigurer的init方法就明白了,具体代码在FormLoginConfigurer的父类AbstractAuthenticationFilterConfigurer里
public void init(B http) throws Exception {
		updateAuthenticationDefaults();
		if (permitAll) {
			PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
		}

		registerDefaultAuthenticationEntryPoint(http);
	}
private void registerDefaultAuthenticationEntryPoint(B http) {
	ExceptionHandlingConfigurer<B> exceptionHandling = http
			.getConfigurer(ExceptionHandlingConfigurer.class);
	if (exceptionHandling == null) {
		return;
	}
	ContentNegotiationStrategy contentNegotiationStrategy = http
			.getSharedObject(ContentNegotiationStrategy.class);
	if (contentNegotiationStrategy == null) {
		contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
	}

	MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(
			contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML,
			new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
	mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
	RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
				new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));

	RequestMatcher preferredMatcher = new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));

	exceptionHandling.defaultAuthenticationEntryPointFor(
				postProcess(authenticationEntryPoint), preferredMatcher);
	}

这样就可以明确看到通过获取到ExceptionHandlingConfigurer对象,并调用defaultAuthenticationEntryPointFor这个方法,就可以把formLogin对应的entrypoint追加到ExceptionHandlingConfigurer配置类中,并且设置了匹配规则。相似的HttpBasicConfigurer也是通过这种形式将基本认证对应的entrypoint追加进来的,当采用cas认证时,spring 没有为我们提供类似的配置类,但是我们可以直接调用下面的方法来设置
http.exceptionHandling().authenticationEntryPoint(casEntryPoint())

另外两个属性accessDeniedHandler和RequestCache的设置就比较简单了
如果配置类中指定了accessDeniedHandler就用,否则就不设置,ExceptionTranslationFilter类中默认使用AccessDeniedHandlerImpl这个实现类,requestCache的设置也比较简单
private RequestCache getRequestCache(H http) {
		RequestCache result = http.getSharedObject(RequestCache.class);
		if (result != null) {
			return result;
		}
		return new HttpSessionRequestCache();
	}

如果指定就用指定的否则默认采用HttpSessionRequestCache。

这样我们的ExceptionTranslationFilter就完全组装好了,并且也作为Filter追加到了servlet中,可以对在鉴权过程中异常进行处理了
分享到:
评论

相关推荐

    spring-security 官方文档 中文版

    - **小结**:这些组件共同构成了 Spring Security 的核心架构,提供了强大的身份验证和授权能力。 **5.3 验证** - **什么是 Spring Security 的验证呢?** - Spring Security 的验证机制允许开发者自定义认证过程...

    spring-security简单demo

    3. **过滤器链(Filter Chain)**:Spring Security 的核心是基于Servlet Filter的过滤器链。这个链上的每个过滤器都有特定的任务,比如`UsernamePasswordAuthenticationFilter`处理登录请求,`...

    spring-security-4.2.0.RELEASE-dist

    3. **过滤器链(Filter Chain)**:Spring Security的核心是过滤器链,它拦截HTTP请求并应用安全策略。4.2.0版本中的过滤器链包括了如`DelegatingFilterProxy`、`ChannelProcessingFilter`、`...

    spring-security3.1源码

    3. **Filter安全链**:Spring Security 的Web安全功能主要通过一系列过滤器实现,这些过滤器构成了安全链。其中关键的过滤器有`DelegatingFilterProxy`、`ChannelProcessingFilter`、`...

    spring-security-3.2.9的jar包和源码包

    Spring Security Filter Chain 包括了如`DelegatingFilterProxy`、`ChannelProcessingFilter`、`SecurityContextHolderAwareRequestFilter`、`AnonymousAuthenticationFilter`、`SessionManagementFilter`、`...

    Spring Security-3.0.1 中文官方文档(翻译版)

    在Web应用中,Spring Security的验证机制涉及ExceptionTranslationFilter、AuthenticationEntryPoint和在请求之间保存SecurityContext。同时,它还涉及到了访问控制(验证),包括安全和AOP建议、安全对象和...

    Spring_Security-3.0.1_中文自学教程.pdf

    - **SecurityContextHolder, SecurityContext 和 Authentication 对象**:这些是 Spring Security 的核心组件。 - **获得当前用户的信息**:通过 SecurityContextHolder 可以轻松获取当前认证用户的信息。 - **...

    Spring_Security-3.0.1_官方文档

    - **SecurityContextHolder, SecurityContext 和 Authentication 对象**:这些组件构成了 Spring Security 的核心架构,负责管理当前用户的认证信息。 - **UserDetailsService**:用于加载用户的详细信息。 - **...

    spring security 3.1.3 源码含sample源码

    Spring Security是Spring框架的一个核心组件,专注于提供身份验证和授权服务,用于构建安全的Java Web应用程序。3.1.3版本是它的一个较早版本,但依然包含了许多关键的安全特性。这个压缩包提供了Spring Security ...

    spring security 参考手册中文版

    核心 - spring-security-core.jar 25 远程处理 - spring-security-remoting.jar 25 Web - spring-security-web.jar 25 配置 - spring-security-config.jar 26 LDAP - spring-security-ldap.jar 26 ACL - spring-...

    Spring Security3技术手册

    - **示例代码**: 在`web.xml`中配置`&lt;filter&gt;`和`&lt;filter-mapping&gt;`,定义Spring Security的入口。 - **1.2 使用命名空间** - Spring Security支持在`spring-security.xml`配置文件中使用特定的命名空间简化配置...

    spring-security-tutorial:Spring Security教程带您逐步学习Spring Security,其中包含大量示例。 Spring Security教程是一本关于Spring Security学习的开源书。利用短暂时间写了本书,图文并茂,用大量实例带你一步一步走进Spring Security的世界

    3. **过滤器链(Filter Chain)**:Spring Security通过一系列过滤器实现其功能。如`HttpSessionAuthenticationStrategy`处理会话认证,`AnonymousAuthenticationFilter`处理匿名用户,`ExceptionTranslationFilter`...

    SpringSecurity 3.0.1.RELEASE.CHM

    1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config -...

    Spring Security 3.0.1 pdf 中文参考文档

    - 需要在 web.xml 中配置 Filter,以便将 Spring Security 的过滤器链加载到应用中。 - **最小 &lt;http&gt; 配置:** - 可以使用 `&lt;http&gt;` 元素来定义基本的安全策略,如登录页面、未授权访问处理等。 - `auto-config`...

    SpringSecurity应用指南

    Spring Security 的核心在于过滤器链,其中`DelegatingFilterProxy`扮演关键角色。这个过滤器代理了其他所有过滤器,并必须在`web.xml`中声明,名为`springSecurityFilterChain`。`DelegatingFilterProxy`按照特定...

    spring中文API文档.docx

    验证管理是Spring Security的核心部分,`AuthenticationManager`负责处理认证请求,而`UserDetailsService`接口用于从数据源获取用户信息。`GrantedAuthority`则表示用户的权限。安全对象和`...

    Spring Security 3.0.1中文官方文档

    在结构和实现方面,文档深入探讨了Spring Security的技术概述,包括运行环境和核心组件,例如SecurityContextHolder、SecurityContext和Authentication对象,这些是管理用户安全上下文的核心工具。...

Global site tag (gtag.js) - Google Analytics