Spring Security Config : HttpSecurity安全配置器 HttpBasicConfigurer

本文深入探讨了Spring Security中HTTP Basic认证配置的细节,包括HttpBasicConfigurer如何配置BasicAuthenticationFilter,以及如何使用AuthenticationEntryPoint和RememberMeServices等共享对象。同时,文章还介绍了如何通过ExceptionHandlingConfigurer和LogoutConfigurer进行定制。

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

概述

介绍

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

  • 配置如下安全过滤器Filter

    • BasicAuthenticationFilter
  • 创建的共享对象

    • AuthenticationEntryPoint

      缺省是 BasicAuthenticationEntryPoint

在配置过程中,HttpBasicConfigurer会使用到如下共享对象 :

  • AuthenticationManager
  • RememberMeServices
  • ContentNegotiationStrategy

也用到了其他安全配置器:

  • ExceptionHandlingConfigurer

    用于注册自己定义的认证入口点

  • LogoutConfigurer

    用于注册一个退出成功处理器(总是返回状态字204)

继承关系

使用

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

源代码

源代码版本 Spring Security Config 5.1.4.RELEASE

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

// 省略 imports


public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
		AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {

    // 用于匹配 ajax 请求的请求匹配器
	private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = 
            new RequestHeaderRequestMatcher("X-Requested-With",	"XMLHttpRequest");

	private static final String DEFAULT_REALM = "Realm";

	private AuthenticationEntryPoint authenticationEntryPoint;
	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
	private BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();

	/**
	 * Creates a new instance
	 * @throws Exception
	 * @see HttpSecurity#httpBasic()
	 */
	public HttpBasicConfigurer() throws Exception {
		realmName(DEFAULT_REALM);

		LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
		entryPoints.put(X_REQUESTED_WITH, new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));

		DelegatingAuthenticationEntryPoint defaultEntryPoint = new DelegatingAuthenticationEntryPoint(
				entryPoints);
		defaultEntryPoint.setDefaultEntryPoint(this.basicAuthEntryPoint);
		this.authenticationEntryPoint = defaultEntryPoint;
	}

	/**
	 * Allows easily changing the realm, but leaving the remaining defaults in place. If
	 *  #authenticationEntryPoint(AuthenticationEntryPoint) has been invoked,
	 * invoking this method will result in an error.
	 *
	 * @param realmName the HTTP Basic realm to use
	 * @return HttpBasicConfigurer for additional customization
	 * @throws Exception
	 */
	public HttpBasicConfigurer<B> realmName(String realmName) throws Exception {
		this.basicAuthEntryPoint.setRealmName(realmName);
		this.basicAuthEntryPoint.afterPropertiesSet();
		return this;
	}

	/**
	 * The AuthenticationEntryPoint to be populated on
	 * BasicAuthenticationFilter in the event that authentication fails. The
	 * default to use BasicAuthenticationEntryPoint with the realm
	 * "Realm".
	 *
	 * @param authenticationEntryPoint the AuthenticationEntryPoint to use
	 * @return HttpBasicConfigurer for additional customization
	 */
	public HttpBasicConfigurer<B> authenticationEntryPoint(
			AuthenticationEntryPoint authenticationEntryPoint) {
		this.authenticationEntryPoint = authenticationEntryPoint;
		return this;
	}

	/**
	 * Specifies a custom AuthenticationDetailsSource to use for basic
	 * authentication. The default is WebAuthenticationDetailsSource.
	 *
	 * @param authenticationDetailsSource the custom AuthenticationDetailsSource
	 * to use
	 * @return HttpBasicConfigurer for additional customization
	 */
	public HttpBasicConfigurer<B> authenticationDetailsSource(
			AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
		this.authenticationDetailsSource = authenticationDetailsSource;
		return this;
	}

    // 初始化方法
	@Override
	public void init(B http) throws Exception {
		registerDefaults(http);
	}

	private void registerDefaults(B http) {
      // 获取共享对象 ContentNegotiationStrategy 或者使用缺省的 HeaderContentNegotiationStrategy
      // 用于构建请求匹配器
		ContentNegotiationStrategy contentNegotiationStrategy = http
				.getSharedObject(ContentNegotiationStrategy.class);
		if (contentNegotiationStrategy == null) {
			contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
		}

       // 构建 REST请求的 请求匹配器 
		MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(
				contentNegotiationStrategy, MediaType.APPLICATION_ATOM_XML,
				MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
				MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML,
				MediaType.MULTIPART_FORM_DATA, MediaType.TEXT_XML);
		restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));

       // 构建 针对所有请求的 请求匹配器 
		MediaTypeRequestMatcher allMatcher = 
		    new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.ALL);       
		allMatcher.setUseEquals(true);

       // 构建针对非HTML媒体类型的 请求匹配器 
		RequestMatcher notHtmlMatcher = new NegatedRequestMatcher(
				new MediaTypeRequestMatcher(contentNegotiationStrategy,
						MediaType.TEXT_HTML));
                        
       // 构建针对非HTML媒体类型的 但又是 REST 请求的 请求匹配器                 
       // 注意,这里其实是对上面两个 请求匹配器的 AND 操作 : restMatcher , notHtmlMatcher
		RequestMatcher restNotHtmlMatcher = new AndRequestMatcher(
				Arrays.<RequestMatcher>asList(notHtmlMatcher, restMatcher));


       // 最终要使用的请求匹配器 :  X_REQUESTED_WITH OR restNotHtmlMatcher OR allMatcher
		RequestMatcher preferredMatcher = 
            new OrRequestMatcher(Arrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher, allMatcher));

		registerDefaultEntryPoint(http, preferredMatcher);
		registerDefaultLogoutSuccessHandler(http, preferredMatcher);
	}

   // 向 ExceptionHandlingConfigurer 登记注册该 HttpBasicConfigurer 的认证入口点
   // authenticationEntryPoint 到相应的请求匹配器 preferredMatcher
	private void registerDefaultEntryPoint(B http, RequestMatcher preferredMatcher) {
		ExceptionHandlingConfigurer<B> exceptionHandling = http
				.getConfigurer(ExceptionHandlingConfigurer.class);
		if (exceptionHandling == null) {
			return;
		}
		exceptionHandling.defaultAuthenticationEntryPointFor(
				postProcess(this.authenticationEntryPoint), preferredMatcher);
	}

   // 向 LogoutConfigurer 登记注册一个 HttpStatusReturningLogoutSuccessHandler 
   // 到相应的请求匹配器 preferredMatcher,退出成功时总是返回状态码 204
	private void registerDefaultLogoutSuccessHandler(B http, RequestMatcher preferredMatcher) {
		LogoutConfigurer<B> logout = http.getConfigurer(LogoutConfigurer.class);
		if (logout == null) {
			return;
		}
		LogoutConfigurer<B> handler = logout.defaultLogoutSuccessHandlerFor(
				    postProcess(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT)), 
				    preferredMatcher);
	}

	// 配置方法
	@Override
	public void configure(B http) throws Exception {
		// 基于共享对象 authenticationManager , rememberMeServices 和 
		// 属性 authenticationEntryPoint 构造 BasicAuthenticationFilter
		// basicAuthenticationFilter, 这是最终要增加到 HttpSecurity 安全构建器 http 上的过滤器
		AuthenticationManager authenticationManager = http
				.getSharedObject(AuthenticationManager.class);
		BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(
				authenticationManager, this.authenticationEntryPoint);
		if (this.authenticationDetailsSource != null) {
			basicAuthenticationFilter
					.setAuthenticationDetailsSource(this.authenticationDetailsSource);
		}
		RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
		if (rememberMeServices != null) {
			basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
		}
		basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
		http.addFilter(basicAuthenticationFilter);
	}
}

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值