Spring Security HTTP认证

news2024/7/6 20:03:35

本文内容来自王松老师的《深入浅出Spring Security》,自己在学习的时候为了加深理解顺手抄录的,有时候还会写一些自己的想法。

 

        HTTP提供了用户权限控制和认证的通用方式,这种认证方式通过HTTP请求头来提供认证信息,而不是通过表单登录。最为小伙伴熟知的应该是HTTP Basic Authentication,另外一种是相对安全的HTTP Digest Authentication。Spring Security中对于这两种认证方式都提供了相应的支持。接下来我们来学习下这两种认证的具体使用方法以及各自的优缺点。

HTTP Basic Authentication

        HTTP Basic Authentication中文翻译为HTTP基本认证。在这种认证方式中,将用户的用户名/密码经过Base64编码之后,放在请求头的Authorization字段中,从而完成于用户的身份证。

        在RFC7235(https://tools.ietf.org/html/rfc7235)规范中定义的认证方式,方客户端发起一个请求之后,服务端可以针对该请求返回一个质询信息,然后客户端再提供用户的凭证信息。具体的质询和应答流程如下图:

         从上图我们可以看到,HTTP基本认证流程是这样的:

  • 首先客户端(浏览器)发起请求
    GET /hello HTTP/1.1
    Host: localhost:8080
  • 服务端收到请求,发现用户没有认证,于是给出如下响应。状态码401表示用户未完成认证,WWW-Authenticate响应头定义了使用何种认证方式去完成身份认证。最简单的、常见的的就是我们使用的HTTP基本认证(Basic)、Bearer(OAuth2.0认证)、Digest(HTTP摘要认证)等等取值。
    HTTP/1.1 401
    WWW-Authenticate: Basic realm="Realm"
  • 客户端收到服务器的响应之后,将用户名/密码使用Base64编码之后,放在请求头中再次发起请求
    GET /hello HTTP/1.1
    Host: localhost:8080
    Authorization: Basic amF2YWJveToxMjM=
  • 服务器端解析Authorization字段,完成用户身份的校验,最后将资环返回给客户端

    HTTP/1.1 200
    Content-Type: text/html;charset=UTF-8
    Content-Length: 16

    上面就是整个HTTP Basic Authenticaiton认证流程。

        可以看到,这种认证方式实际上非常简单,基本上所有的浏览器上都支持这种认证方式。HTTP基本认证方式没有对传输的凭证信息进行加密,仅仅只是进行了Base64编码,这就造成了很大的安全隐患,所以如果用到HTTP基本的认证一般都是结合HTTPS一起使用。同时,一旦使用HTTP基本认证,除非用户关闭浏览器或者清空浏览器缓存,否则没有办法退出登录。

基本用法

        Spring Security中开启HTTP基本认证非常容易,继承WebSecurityConfigurerAdapter重写configure(HttpSecurity http) 方法,配置配置如下:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated() 
                .and()
                .httpBasic()
                .and().httpBasic()
        ; 
    }

        通过httpBasic()方法即可开启HTTP基本认证。配置完成之后,启动项目,此时如果要访问一个受保护的资源,浏览器会自动弹出一个认证框。输入正确的用户名/密码认证成功之后,就可以访问受保护的资源了。

        

 源码分析

        接下来我们看看Spring Security中是如何实现HTTP基本认证的。实现整体分为两部分:

  • 对没有认证的请求发出咨询
  • 解析携带了认证信息的请求

质询

         httpBasic( )方法开启了HTTP基本认证的配置,具体通过配置HttpBasicConfigurer类来完成。在HttpBasicConfigurer配置类的init方法中调用了registerDefaultEntryPoint方法,该方法完成了失败请求处理类AuthenticationEntryPoint类的配置,代码如下:

	private void registerDefaultEntryPoint(B http, RequestMatcher preferredMatcher) {
		ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
		if (exceptionHandling == null) {
			return;
		}
		exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(this.authenticationEntryPoint),
				preferredMatcher);
	}

        这里配置到exceptionHandling中的authenticationEntryPoint是一个代理对象,具体代理的是BasicAuthenticationEntryPoint(启动项目的是debug时就能看到)。

        简而言之,如果一个请求没有携带认证信息的话,最终将被BasicAuthenticationEntryPoint实例处理,我们来看下该类的主要实现:

public class BasicAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {

	private String realmName;

	@Override
	public void afterPropertiesSet() {
		Assert.hasText(this.realmName, "realmName must be specified");
	}

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException {
		response.addHeader("WWW-Authenticate", "Basic realm=\"" + this.realmName + "\"");
		response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
	}

	public String getRealmName() {
		return this.realmName;
	}

	public void setRealmName(String realmName) {
		this.realmName = realmName;
	}
}

        可以看到,这个类的处理逻辑还是很简单的,响应头中添加WWW-Authenticate字段,然后发送错误响应码为401。

        这里就是发出质询的代码。总结一下,就是一个未经认证的请求,在经过Spring Security过滤器链时会抛出异常,该异常会在ExceptionTranslationFilter过滤器链中调用BasicAuthenticationEntryPoint的commence方法中进行处理。

请求分析

        HttpBasicConfigurer类中的configure方法中,向Spring Security过滤器链中添加一个过滤器BasicAuthenticationFilter,改过滤器专门来处理HTTP基本认证相关的事情,我们来看一下它的核心方法doFilterInternal:

@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		try {
			UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request);
			if (authRequest == null) {
				this.logger.trace("Did not process authentication request since failed to find "
						+ "username and password in Basic Authorization header");
				chain.doFilter(request, response);
				return;
			}
			String username = authRequest.getName();
			this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
			if (authenticationIsRequired(username)) {
				Authentication authResult = this.authenticationManager.authenticate(authRequest);
				SecurityContext context = SecurityContextHolder.createEmptyContext();
				context.setAuthentication(authResult);
				SecurityContextHolder.setContext(context);
				if (this.logger.isDebugEnabled()) {
					this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
				}
				this.rememberMeServices.loginSuccess(request, response, authResult);
				this.securityContextRepository.saveContext(context, request, response);
				onSuccessfulAuthentication(request, response, authResult);
			}
		}
		catch (AuthenticationException ex) {
			SecurityContextHolder.clearContext();
			this.logger.debug("Failed to process authentication request", ex);
			this.rememberMeServices.loginFail(request, response);
			onUnsuccessfulAuthentication(request, response, ex);
			if (this.ignoreFailure) {
				chain.doFilter(request, response);
			}
			else {
				this.authenticationEntryPoint.commence(request, response, ex);
			}
			return;
		}
		chain.doFilter(request, response);
	}

        该方法执行流程如下:

  1. 首先调用authenticationConverter的convert方法从请求头中的Authorization字段进行解析,经过Base64解码之后的用户名和密码是一个用 ":" 隔开的字符串,例如用户使用tianluhua/123进行登录,那么这里解码后得到的字符串就是tianluhua:123,然后根据用户名和密码构造一个UsernamePasswordAuthenticationToken对象出来。
  2. authRequest 为null,说明请求头中没有包含Http基本认证的信息,改方法到此为止。那么接下来执行其他的过滤器即可。
  3. 从authRequest对象中提取出用户名,然后调用authenticationIsRequired方法判断是否有必要进行认证,如果没必要认证就执行下面的过滤器。
  4. 如果有必要认证,就调用authenticationManager的authenticate方法完成用户认证,同时将信息存入SecurityContextHolder中;如果配合了rememberMeServices,也行进相应的处理,最后回调一个onSuccessfulAuthentication方法,不过改方法没有做任何实现。
  5. 如果认证过程中抛出异常,则进行相应的处理即可
  6. 最后执行接下来的过滤器。在后续的过滤器中执行的过程中,由于SecurityContextHolder中已经保存了登录用户的信息了,相当于用户已经完成了登录了,因此就和普通的请求一样,不会被“半路拦截”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/65143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]计算机毕业设计基于Springboot校园运动会管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

opencv c++ 霍夫圆检测

1、原理 a&#xff09;对某点&#xff0c;以其为圆心的圆为无数&#xff08;一圈圈的圆&#xff09;&#xff0c;将其从x-y平面坐标系上转换到r-θ极坐标系上后&#xff0c;则变成了以r、θ为自变量&#xff0c;为固定值&#xff0c;x、y为因变量的式子&#xff1a; b&#xff…

一文带你走进JS语法(最全笔记)

目录 基本语法 1.引入方式 2.注释 3.输入输出语句 4.变量和常量 5.原始数据类型 6.运算符 7.流程控制语句 8.数组 9.函数 DOM 1.概述 2.元素对象的操作 3.元素内属性操作 4.元素内文本操作 事件 面向对象 1.定义类的方式 2.继承 内置对象 1.Number对象 2…

matlab贝叶斯隐马尔可夫hmm模型实现

贝叶斯隐马尔可夫模型是一种用于分割连续多变量数据的概率模型。该模型将数据解释为一系列隐藏状态生成。每个状态都是重尾分布的有限混合&#xff0c;具有特定于状态的混合比例和共享的位置/分散参数。 相关视频&#xff1a;马尔可夫链原理可视化解释与R语言区制转换Markov r…

java面试总结

文章目录JVM类的加载过程类加载器有哪些什么是双亲委派双亲委派的好处如何打破双亲委派java内存模型栈帧的结构java堆的分代设计对象内存分配对应的GC为什么需要Survivor区?只有Eden不行吗&#xff1f;为什么要有两个Survivor区对象创建过程对象内存布局对象头Mark Word对象大…

提高 K8S 容器运行时的可观察性最佳方法之一

当谈到云原生可观察性时&#xff0c;可能每个人都会提到OpenTelemetry (OTEL)&#xff0c;因为社区需要依赖标准来将所有集群组件开发指向到同一方向。OpenTelemetry 使我们能够将日志、指标&#xff08;metrics&#xff09;、跟踪&#xff08;traces&#xff09;和其他上下文信…

内容爆炸时代,如何打造品牌经营的“弹药库”?

&#x1f446;点击一键预约本周三主题直播&#x1f446;2017年&#xff0c;华为总裁办发布《华为之熵&#xff0c;光明之矢》的内部学习邮件&#xff0c;将热力学中“熵”的概念应用到企业管理中&#xff0c;成为被人们津津乐道的“熵减哲学”。对于“熵”的概念&#xff0c;大…

[附源码]计算机毕业设计基于vuejs的文创产品销售平台app

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]计算机毕业设计甜品购物网站Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【OpenCV-Python】教程:4-2 Harris角点检测

OpenCV Python Harris 角点检测 【目标】 理解Harris角点检测背后的概念&#xff1b;cv2.cornerHarris(), cv2.cornerSubPix() 【理论】 上一章节中&#xff0c;我们看到在图像中每个方向变化都很大的区域就是角点&#xff0c;一个早期的尝试是由 Chris Harris & Mike …

关闭图片窗口

关闭图片窗口 结果演示 概述 通过事件的绑定来实现&#xff0c;关闭网页中某个图片窗口的效果。 构建HTML框架 <body><div class"box">图片<img src"https://upload-bbs.mihoyo.com/upload/2021/03/11/73281682/f810fbc2e4806aab8176e96feee…

关于数据分析知识的干货分享

数据分析的出现是因为人类难以理解海量数据所呈现出来的信息&#xff0c;不能从中找到相应的规律来对现实中的事物进行对应&#xff0c;我们都知道数据有很高的价值&#xff0c;但不能利用的价值&#xff0c;没有任何意义。 为了解决这一问题&#xff0c;数据分析在长期的数据…

超透镜与超表面全息

超透镜和超表面因其操纵电磁场的独特特性而在科学上声名鹊起&#xff0c;如今它们的制造已经变得可行。但它们的设计难度远远超过了传统镜片&#xff0c;因为必须考虑到纳米级构件的特性。 VirtualLab Fusion的优势  统一的平台&#xff1a;具有将纳米级构建模块和大尺…

JAVA-GUI工具的编写-----事件篇

上一节介绍了HTTP以及HTTPS请求&#xff0c;那么这里我们就接着讲解事件与请求联动。 关于POC以及EXP最大的区别就是&#xff0c;EXP是附带利用功能&#xff0c;而POC仅仅是检测功能&#xff0c;所以这里我们需要动起来&#xff0c;GUI小工具能用上的事件功能其实就两个&#…

【vue3】代码自动格式化和volar卡顿问题解决

一、格式化策略 用eslint做代码检查和格式化是很方便的东西&#xff1b; 这里我们使用vscode完成这些操作&#xff1b; 在代码保存的时候&#xff0c;顺便完成格式化操作 1)装上eslint和prettier插件 2)装完插件之后&#xff0c;我们需要配置一下 打开 文件 > 首选项 >…

为什么阿里巴巴建议HashMap初始化时需要指定容量大小?

为什么阿里巴巴建议HashMap初始化时需要指定容量大小&#xff1f; 为什么&#xff1f; 关于集合类&#xff0c;《阿里巴巴Java开发手册》中写道&#xff1a; 我们先来写一段代码在JDK 1.7 &#xff08;jdk1.7.0_80&#xff09;下面来分别测试下&#xff0c;在不指定初始化容量…

Docker harbor私有仓库部署与管理

Docker harbor私有仓库部署与管理Docker harbor私有仓库部署与管理一、Docker 私有仓库1、下载registry镜像2、修改配置文件/etc/docker/daemon.json &#xff0c;添加私有仓库配置&#xff0c;修改完后重启docker3、创建私有仓库容器4、推送镜像到私有仓库中5、查看当前仓库的…

使用 Lua 脚本和海康 VisionMaster 进行 TCP 通信

说明&#xff1a;因任务需求&#xff0c;需要进行海康VisionMaster服务端和Lua脚本客户端进行TCP通信传输数据。因为之前从未接触过Lua语言&#xff0c;所以也趁机学习一波。 内容Lua教程手册LuaSocket使用方法一方法二报错&#xff1a;“attempt to compare number with strin…

高级_09.性能分析工具的使用

第09章_性能分析工具的使用 1. 数据库服务器的优化步骤 当我们遇到数据库调优问题的时候&#xff0c;该如何思考呢&#xff1f;这里把思考的流程整理成下面这张图。 整个流程划分成了观察&#xff08;Show status&#xff09;和行动&#xff08;Action&#xff09;两个部分。…

Service详解「2」

Service详解「2」 文章目录Service详解「2」Service介绍kube-proxy目前支持三种工作模式:userspace 模式iptables 模式ipvs 模式Service类型Service使用实验环境准备ClusterIP类型的ServiceEndpoint负载分发策略HeadLiness类型的ServiceNodePort类型的ServiceLoadBalancer类型的…