Spring Security Config : HttpSecurity安全配置器 ExceptionHandlingConfigurer

博客围绕Spring Security Config展开,介绍其作为配置任务时需配置安全过滤器,使用共享对象等情况。阐述了继承关系,还给出对象创建和通过配置的使用示例,提及源代码版本为5.1.4.RELEASE,并给出参考文章相关信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

介绍

作为一个配置HttpSecuritySecurityConfigurer,ExceptionHandlingConfigurer的配置任务如下 :

  • 配置如下安全过滤器Filter
    • ExceptionTranslationFilter

ExceptionHandlingConfigurer配置过程中使用到了如下共享对象 :

  • RequestCache

    如果该共享对象不存在,则缺省使用一个HttpSessionRequestCache

  • AuthenticationEntryPoint

继承关系

ExceptionHandlingConfigurer

使用

例子 1 : ExceptionHandlingConfigurer对象的创建

	// HttpSecurity 代码片段
	public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {
		return getOrApply(new ExceptionHandlingConfigurer<>());
	}

例子 2 : 通过ExceptionHandlingConfigurer配置httpSecurity

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	// ...
	@Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.exceptionHandling()
            .authenticationEntryPoint(new MyLoginUrlAuthenticationEntryPoint("/custom-login"))
            .accessDeniedHandler(new MyAccessDeniedHandler());
    }
	// ...	
}

源代码

源代码版本 Spring Security Config 5.1.4.RELEASE

package org.springframework.security.config.annotation.web.configurers;

// 省略 imports

public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> extends
		AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>, H> {

	private AuthenticationEntryPoint authenticationEntryPoint;

	private AccessDeniedHandler accessDeniedHandler;

	private LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> defaultEntryPointMappings = 
            new LinkedHashMap<>();

	private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = 
            new LinkedHashMap<>();

	/**
	 * Creates a new instance
	 * @see HttpSecurity#exceptionHandling()
	 */
	public ExceptionHandlingConfigurer() {
	}

	/**
	 * Shortcut to specify the AccessDeniedHandler to be used is a specific error
	 * page
	 * 快捷方式,设置访问被拒绝使用的 AccessDeniedHandler 是一个 使用 AccessDeniedHandlerImpl ,
     * 并且指向设定的错误页面(比如 /errors/401) accessDeniedUrl :
	 * 1. 如果 accessDeniedUrl 为 null,则返回 403 给浏览器端
	 * 2. 如果 accessDeniedUrl 不为 null,是某个 / 开头的有效路径,则 foward 用户到相应的错误页面
	 * @param accessDeniedUrl the URL to the access denied page (i.e. /errors/401)
	 * @return the ExceptionHandlingConfigurer for further customization
	 * @see AccessDeniedHandlerImpl
	 * @see #accessDeniedHandler(org.springframework.security.web.access.AccessDeniedHandler)
	 */
	public ExceptionHandlingConfigurer<H> accessDeniedPage(String accessDeniedUrl) {
		AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
		accessDeniedHandler.setErrorPage(accessDeniedUrl);
		return accessDeniedHandler(accessDeniedHandler);
	}

	/**
	 * Specifies the AccessDeniedHandler to be used
     * 设置访问被拒绝时使用的 AccessDeniedHandler
	 *
	 * @param accessDeniedHandler the AccessDeniedHandler to be used
	 * @return the ExceptionHandlingConfigurer for further customization
	 */
	public ExceptionHandlingConfigurer<H> accessDeniedHandler(
			AccessDeniedHandler accessDeniedHandler) {
		this.accessDeniedHandler = accessDeniedHandler;
		return this;
	}

	/**
	 * Sets a default AccessDeniedHandler to be used which prefers being
	 * invoked for the provided RequestMatcher. If only a single default
	 * AccessDeniedHandler is specified, it will be what is used for the
	 * default AccessDeniedHandler. If multiple default
	 * AccessDeniedHandler instances are configured, then a
	 * RequestMatcherDelegatingAccessDeniedHandler will be used.
	 *
	 * @param deniedHandler the AccessDeniedHandler to use
	 * @param preferredMatcher the RequestMatcher for this default
	 * AccessDeniedHandler
	 * @return the ExceptionHandlingConfigurer for further customizations
	 * @since 5.1
	 */
	public ExceptionHandlingConfigurer<H> defaultAccessDeniedHandlerFor(
			AccessDeniedHandler deniedHandler, RequestMatcher preferredMatcher) {
		this.defaultDeniedHandlerMappings.put(preferredMatcher, deniedHandler);
		return this;
	}

	/**
	 * Sets the AuthenticationEntryPoint to be used.
	 *
	 * 
	 * If no #authenticationEntryPoint(AuthenticationEntryPoint) is specified,
	 * then
	 * #defaultAuthenticationEntryPointFor(AuthenticationEntryPoint, RequestMatcher)
	 * will be used. The first AuthenticationEntryPoint will be used as the
	 * default is no matches were found.
	 * 
	 *
	 * 
	 * If that is not provided defaults to Http403ForbiddenEntryPoint.
	 * 
	 *
	 * @param authenticationEntryPoint the AuthenticationEntryPoint to use
	 * @return the ExceptionHandlingConfigurer for further customizations
	 */
	public ExceptionHandlingConfigurer<H> authenticationEntryPoint(
			AuthenticationEntryPoint authenticationEntryPoint) {
		this.authenticationEntryPoint = authenticationEntryPoint;
		return this;
	}

	/**
	 * Sets a default AuthenticationEntryPoint to be used which prefers being
	 * invoked for the provided RequestMatcher. If only a single default
	 * AuthenticationEntryPoint is specified, it will be what is used for the
	 * default AuthenticationEntryPoint. If multiple default
	 * AuthenticationEntryPoint instances are configured, then a
	 * DelegatingAuthenticationEntryPoint will be used.
	 *
	 * @param entryPoint the AuthenticationEntryPoint to use
	 * @param preferredMatcher the RequestMatcher for this default
	 * AuthenticationEntryPoint
	 * @return the ExceptionHandlingConfigurer for further customizations
	 */
	public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(
			AuthenticationEntryPoint entryPoint, RequestMatcher preferredMatcher) {
		this.defaultEntryPointMappings.put(preferredMatcher, entryPoint);
		return this;
	}

	/**
	 * Gets any explicitly configured AuthenticationEntryPoint
	 * @return
	 */
	AuthenticationEntryPoint getAuthenticationEntryPoint() {
		return this.authenticationEntryPoint;
	}

	/**
	 * Gets the AccessDeniedHandler that is configured.
	 *
	 * @return the AccessDeniedHandler
	 */
	AccessDeniedHandler getAccessDeniedHandler() {
		return this.accessDeniedHandler;
	}

    // SecurityConfigurer 所定义的配置方法:生成一个 ExceptionTranslationFilter 配置到目标安全构建器
	@Override
	public void configure(H http) throws Exception {
      // 准备最终要应用到目标 ExceptionTranslationFilter 的 AuthenticationEntryPoint,
      // 先尝试使用外部指定值,未指定的话使用缺省值
		AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
       // 创建目标 ExceptionTranslationFilter 
       // 这里使用到了 RequestCache, 获取该 RequestCache 对象的过程是 :
       // 先尝试使用 http 中的共享对象,如果没有找到,则使用缺省值, 新建一个 HttpSessionRequestCache 对象
		ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
				entryPoint, getRequestCache(http));
       // 准备访问被拒绝时要是用的处理器 AccessDeniedHandler
       // 先尝试使用外部指定值,未指定的话使用缺省值 
		AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
		exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
        
       // 后置处理目标 ExceptionTranslationFilter 
		exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
       // 将目标 ExceptionTranslationFilter 添加到目标安全构建器 http 
		http.addFilter(exceptionTranslationFilter);
	}

	/**
	 * Gets the AccessDeniedHandler according to the rules specified by
	 * #accessDeniedHandler(AccessDeniedHandler)
	 * @param http the HttpSecurity used to look up shared
	 * AccessDeniedHandler
	 * @return the AccessDeniedHandler to use
	 */
	AccessDeniedHandler getAccessDeniedHandler(H http) {
		AccessDeniedHandler deniedHandler = this.accessDeniedHandler;
		if (deniedHandler == null) {
			deniedHandler = createDefaultDeniedHandler(http);
		}
		return deniedHandler;
	}

	/**
	 * Gets the AuthenticationEntryPoint according to the rules specified by
	 * #authenticationEntryPoint(AuthenticationEntryPoint)
	 * @param http the HttpSecurity used to look up shared
	 * AuthenticationEntryPoint
	 * @return the AuthenticationEntryPoint to use
	 */
	AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
		AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
		if (entryPoint == null) {
			entryPoint = createDefaultEntryPoint(http);
		}
		return entryPoint;
	}

    // 创建缺省使用的 AccessDeniedHandler : 访问被拒绝时的处理器 :
    // 1. 如果 this.defaultDeniedHandlerMappings 为空,则是用一个新的 AccessDeniedHandlerImpl
    // 对象:访问被拒绝时想浏览器返回状态字 403
    // 2. 如果 this.defaultDeniedHandlerMappings 包含一个元素,这是用该元素;
    // 3. 如果 this.defaultDeniedHandlerMappings 包含多个元素,则构造一个 
    // RequestMatcherDelegatingAccessDeniedHandler 对象包装和代理 
    // this.defaultDeniedHandlerMappings 中的这组元素,此 RequestMatcherDelegatingAccessDeniedHandler
    // 缺省的 AccessDeniedHandler 则是一个新的 AccessDeniedHandlerImpl
    // 对象:访问被拒绝时想浏览器返回状态字 403
	private AccessDeniedHandler createDefaultDeniedHandler(H http) {
		if (this.defaultDeniedHandlerMappings.isEmpty()) {
			return new AccessDeniedHandlerImpl();
		}
		if (this.defaultDeniedHandlerMappings.size() == 1) {
			return this.defaultDeniedHandlerMappings.values().iterator().next();
		}
		return new RequestMatcherDelegatingAccessDeniedHandler(
				this.defaultDeniedHandlerMappings,
				new AccessDeniedHandlerImpl());
	}

    // 创建缺省使用的 AuthenticationEntryPoint :
    // 1. 如果 this.defaultEntryPointMappings 为空,则使用一个 Http403ForbiddenEntryPoint 实例
    // 2. 如果 this.defaultEntryPointMappings 只包含一个元素,直接使用该元素
    // 3. 如果 this.defaultEntryPointMappings 有多个元素,构建一个 DelegatingAuthenticationEntryPoint
    // 代理对象供使用,该代理对象也是一个 AuthenticationEntryPoint,它将任务代理给 
    // this.defaultEntryPointMappings 中的各个 AuthenticationEntryPoint 对象,并将其中第一个设置为
    // 缺省
	private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
		if (this.defaultEntryPointMappings.isEmpty()) {
			return new Http403ForbiddenEntryPoint();
		}
		if (this.defaultEntryPointMappings.size() == 1) {
			return this.defaultEntryPointMappings.values().iterator().next();
		}
		DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(
				this.defaultEntryPointMappings);
		entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator()
				.next());
		return entryPoint;
	}

	/**
	 * Gets the RequestCache to use. If one is defined using
	 * #requestCache(org.springframework.security.web.savedrequest.RequestCache),
	 * then it is used. Otherwise, an attempt to find a RequestCache shared object
	 * is made. If that fails, an HttpSessionRequestCache is used
	 *
	 * @param http the HttpSecurity to attempt to fined the shared object
	 * @return the RequestCache to use
	 */
	private RequestCache getRequestCache(H http) {
		RequestCache result = http.getSharedObject(RequestCache.class);
		if (result != null) {
			return result;
		}
		return new HttpSessionRequestCache();
	}
}

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值