登录工程:传统 Web 应用中的身份验证技术

标题中 “传统 Web 应用” 这一说法也并没有什么官方定义,只是为了与“现代化 Web 应用”形成比较而自拟的一个概念。所谓现代化 Web 应用指的是那些基于分布式架构思想设计的,面向多个端提供稳定可靠的高可用服务,并且在需要时能够横向扩展的 Web 应用。相对而言,传统 Web 应用则主要是直接面向 PC 用户的 Web 应用程序,采用单体架构较多,也可能在内部采用 SOA 的分布式运算技术。

一直以来,传统 Web 应用为构成互联网发挥了重要作用。因此传统 Web 应用中的身份验证技术经过了几代的发展,已经解决了不少实际问题,并最终沉淀了一些实践模式。


安全议题不容忽视

在讲述多种身份鉴权技术之前,要强调一点:在构建互联网 Web 应用过程中,无论使用哪种技术,在传输用户名和密码时,请一定要采用安全连接。因为无论采用何种鉴权模型,都无法保护用户凭据在传输过程中不被窃取。


Basic 和 Digest 鉴权


基于 HTTP 的 Web 应用离不开 HTTP 本身的安全特性中关于身份鉴权的部分。虽然 HTTP 标准定义了好几种鉴权方式,但真正供 Web 应用开发者选择的并不多,这里简要回顾一下曾经被广泛运用过的 Basic 和 Digest 鉴权。

不知道读者是否熟悉一种最直接向服务器提供身份的方式,即在 URL 中直接写上用户名和密码:

http://user:passwd@www.server.com/index.html

这就是 Basic 鉴权的一种形式。

Basic 和 Digest 是通过在 HTTP 请求头中直接包含用户名和密码,或者它们的哈希值来向服务器传输用户凭据的方法。Basic 鉴权直接在每个请求请求的头部或 URL 中包含明文的用户名或密码,或者经过 Base64 编码过的用户名或密码;而 Digest 则会使用服务器返回的随机值,对用户名和密码拼装后,使用多次 MD5 哈希处理后再向服务器传输。服务器在处理每个请求之前,读取收到的凭据,并鉴定用户的身份。

Basic 和 Digest 鉴权有一系列的缺陷。它们需要在每个请求中提供凭据,因此提供“记住登录状态”功能的网站中,不得不将用户凭据缓存在浏览器中,增加了用户的安全风险。Basic 鉴权基本不对用户名和密码等敏感信息进行预处理,所以只适合于较安全的安全环境,如通过 HTTPS 安全连接传输,或者局域网。看起来更安全的 Digest 在非安全连接传输过程中,也无法抵御中间人通过篡改响应来要求客户端降级为 Basic 鉴权的攻击。Digest 鉴权还有一个缺陷:由于在服务器端需要核对收到的、由客户端经过多次 MD5 哈希值的合法性,需要使用原始密码做相同的运算,这让服务器无法在存储密码之前对其进行不可逆的加密。Basic 和 Digest 鉴权的缺陷决定了它们不可能在互联网 Web 应用中被大量采用。


简单实用的登录技术


对于互联网 Web 应用来说,不采用 Basic 或 Digest 鉴权的理由主要有两个:

1. 不能接受在每个请求中发送用户名和密码凭据
2. 需要在服务器端对密码进行不可逆的加密

因此,互联网 Web 应用开发已经形成了一个基本的实践模式,能够在服务端对密码强加密之后存储,并且尽量减少鉴权过程中对凭据的传输。其过程如下图所示:


基于 Cookie 和 Session  的鉴权过程

这一过程的原理很简单,专门发送一个鉴权请求,只在这个请求中包含原始用户名和密码凭据,经服务器验证合法之后,由服务器发给一个会话标识(Session ID),客户端将会话标识存储在 Cookie 中,服务器记录会话标识与经过验证的用户的对应关系;后续客户端使用会话标识、而不是原始凭据去与服务器交互,服务器读取到会话标识后从自身的会话存储中读取已在第一个鉴权请求中验证过的用户身份。为了保护用户的原始凭据在传输中的安全,只需要为第一个鉴权请求构建安全连接支持。

服务端的代码包含首次鉴权和后续检查并授权访问的过程:

IUser user;
if( validateLogin( nameFromReq, pwdFromReq, out user )){
    Session["CurrentUser"] = user;
}

(首次鉴权)

IUser user = Session["CurrentUser"] as IUser;
if( user == null ){
    Response.Redirect( "/login?return_uri=" + Request.Url.ToString() );
    return;
}

(后续检查并拒绝未识别的用户)

类似这样的技术简易方便,容易操作,因此大量被运用于很多互联网 Web 应用中。它在客户端和传输凭据过程中几乎没有做特殊处理,所以在这两个环节尤其要注意对用户凭据的保护。不过,随着我们对系统的要求越来越复杂,这样简易的实现方式也有一些明显的不足。比如,如果不加以封装,很容易出现在服务器应用程序代码中出现大量对用户身份的重复检查、错误的重定向等;不过最明显的问题可能是对服务器会话存储的依赖,服务器程序的会话存储往往在服务器程序重启之后丢失,因此可能会导致用户突然被登出的情况。虽然可以引入单独的会话存储程序来避免这类问题,但引入一个新的中间件就会增加系统的复杂性。


传统 Web 应用中身份验证最佳实践


上文提到的简单实用的登录技术已经可以帮助建立对用户身份验证的基本图景,在一些简单的应用场景中已经足够满足需求了。然而,用户鉴权就是那种“你可以有很多种方法,就是不怎么优雅” 的问题。

最佳实践指的是那些经过了大量验证,被证明有用的方法。而用户鉴权的最佳实践就是使用自包含的、含有加密内容的 Cookie 作为替代凭据。其鉴权过程与上文所提到基于会话标识的技术没有什么区别,而主要区别在于不再颁发会话标识,取而代之的是一个代表身份的、经过加密的 “身份 Cookie”。

1. 只在鉴权请求中发送一次用户名和密码凭据
2. 成功凭据之后,由服务器生成代表用户身份的 Cookie,发送给客户端
3. 客户端在后续请求中携带上一步中收到的 “身份 Cookie”
4. 服务器解密"身份 Cookie",并对需要访问的资源予以授权

这样,我们消除了对服务器会话存储的依赖,Cookie 本身就有有效期的概念,因此顺便能够轻松提供“记住登录状态”的功能。

另外,由于解密 Cookie 既而检查用户身份的操作相对繁琐,迫使工程师不得不考虑对其抽取专门的服务,最终采用了面向切面的模式对身份验证的过程进行了封装,而开发时只需要使用一些特性标注(Attribute Annotation)对特定资源予以标记即可轻松完成身份验证预处理。


传统 Web 应用中的单点登录


单点登录的需求在向用户提供多种服务的企业普遍存在,出发点是希望用户在一个站点中登录之后,在其他兄弟站点中就不需要再次登录。

如果多个子站所在顶级域名一致,基于上文所述的实践,可以基于 Cookie 共享实现最简单的单点登录:在多个子站中使用相同的加密、解密配置,并且在用户登录成功后设置身份 Cookie 时将 domain 值设置为顶级域名即可。这样,只要在其中一个网站登录,其身份 Cookie 将在用户访问其他子站时也一起带上。不过实际情况中,这个方案的应用场景很有限,毕竟各个子站使用的用户数据模型可能不完全一致,而加密密钥多处共享也增加了服务器应用程序的安全风险。

对于单点登录需求来说,域名相同与否并不是最大的挑战,集成登录系统对各个子站点的系统在设计上的影响才是。我们希望便利用户的同时,也期待各个子系统仍拥有独立用户身份、独立管理和运维的灵活性。因此我们引入独立的鉴权子站点。当用户到达业务站点 A 时,被重定向到鉴权站点;登录成功之后,用户被重定向回到业务站点 A、同时附加一个指示“已有用户登录”的令牌串——此时业务站点 A 使用令牌串,在服务器端从鉴权子站点查询并记录当前已登录的用户。当用户到达业务站点 B 时,执行相同流程。由于已有用户登录,所以用户登录的过程会被自动省略。

这样的单点登录系统能够较好地解决在多个站点中共享用户登录状态的需求。不过,如果在编程实践过程中略有差池,就会让用户陷入巨大的安全风险中。例如,在上述重定向过程中,一旦鉴权系统未能验证返回 URL 的合法性,就容易导致用户被钓鱼网站利用。在传统 Web 应用开发实践中,被广泛部署的身份验证体系是比较重量级的 WS-Federation 和相对轻量级的 OpenID 等技术。


总结

本文简要总结了传统 Web 应用中被广泛使用的几种典型的用户登录时的鉴权处理流程。总体来说,在单体 Web 应用中,身份验证过程并不复杂,只要稍加管理,可以较轻松地解决用户鉴权的问题。但在传统 Web 应用中,为了解决单点登录的需求,人们也尝试了多种方式,最终仍然只有使用一些较复杂的方案才能较好地解决问题。

在现代化 Web 应用中,围绕登录这一需求,俨然已经衍生出了一个新的工程。“登录工程” 并不简单,在后续篇目中将会介绍现代化 Web 应用的典型需求及解决方法。


原文地址:http://www.jianshu.com/p/6ca51a8d66bd


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

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

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

相关文章

getSerializableExtra

Activity之间通过Intent传递值,支持基本数据类型和String对象及它们的数组对象byte、byte[]、char、char[]、boolean、boolean[]、short、short[]、int、int[]、long、long[]、float、float[]、double、double[]、String、String[],还有实现Serializable…

Java 读写图像

转载自 Java 读写图像Java中进行图像I/O(即读图片和写图片,不涉及到复杂图像处理)有三个方法:1. Java Image I/O API,支持常见图片,从Java 2 version 1.4.0开始就内置了。主页:http://java.sun…

powerdesigner在工作时用到的方法

一、所有表中name同步comment的方法: 操作步骤:工具>execute commands>Edit/Run script Option Explicit ; 将下面脚本复制进去并执行 ValidationMode True InteractiveMode im_Batch Dim mdl the current modelget the current …

大数据项目实践:基于hadoop+spark+mongodb+mysql开发医院临床知识库系统

一、前言 从20世纪90年代数字化医院概念提出到至今的20多年时间,数字化医院(Digital Hospital)在国内各大医院飞速的普及推广发展,并取得骄人成绩。不但有数字化医院管理信息系统(HIS)、影像存档和通信系统(PACS)、电子…

两个对象集合根据对象的某个属性进行过滤,返回不同的部分

例如&#xff1a; plants 和houses 两个集合&#xff0c;进行对比过滤&#xff0c;取出plants里和houses不同的部分&#xff0c;并返回。 List<ImprovedSeedPlant> plants improvedSeedPlantMapper.selectList(spWrapper); List<StudsheepSheepHouse> houses h…

简历

技术岗&#xff01; 那么你应该写什么&#xff1f; 简单的介绍项目的规模&#xff0c;有多少技术人员&#xff0c;是互联网项目还是企业软件&#xff0c;这些就可以了。 接下来你需要写关于项目技术的那部分。 项目用了什么框架&#xff1f;spring 还是struts2 ,持久化是用的是…

java drawimage()方法

转载自 java drawimage()方法图像使用的支持分布于java.applet, java.awt, 和 java.awt.image包中。每一个图像都用一个 java.awt.Image 对象表示。除了Image 类外&#xff0c;java.awt 包提供了其它的基本的图像支持&#xff0c;例如Graphics 类的 drawImage方法&#xff0c;…

老师不能把你怎样,但外面的世界可以!

来源 | 菁优网 本文是一位老师讲述的三段小故事&#xff0c;告诉各位家长及孩子&#xff1a; 亲爱的孩子&#xff0c;老师是不能把你怎样&#xff0c;但外面的世界可以。 1. 我有一个学生&#xff0c;喜欢钻研奥数&#xff0c;却走路慢慢吞吞总爱迟到&#xff0c;同学给他起了…

TagHelper是怎么实现的

众所周知&#xff0c;在asp.net core中编写Razor视图的时候&#xff0c;用了一种新的写法--TagHelper 那这个TagHelper是怎么回事呢? 首先来看看TagHelper的项目位置&#xff0c;它是位于Microsoft.AspNetCore.Mvc.TagHelpers。 如果看到project.json&#xff0c;可以发现&…

java电商面试介绍

写博客 前两个项目采用的是ssh框架搭建的&#xff0c;最近的项目采用的是ssm框架搭建的。在实际开发中&#xff0c;我觉得这两个框架&#xff0c;他们最大的区别在于hibernate与mybatis的区别。 Hibernate与mybatis相比较&#xff0c;mybatis更为轻便、灵活&#xff0c;容易…

java 程序实现对图片的压缩生成缩略图并可设定长宽、尺寸压缩率、图片质量

转载自 java 程序实现对图片的压缩生成缩略图并可设定长宽、尺寸压缩率、图片质量 之前是在另一位高手的上传内容中学习到的&#xff0c;并将其代码根据我的需求进行了修改&#xff0c;参考位置&#xff1a;http://jiangpin1987.javaeye.com/blog/749690参考代码&#xff1a;…

List<Map<String,Object>>集合按照map中的某个值进行排序

List<Map<String,Object>>集合按照map中的某个值进行排序 举例&#xff1a; public void test5(){List<Map<String, Object>> list new ArrayList<Map<String, Object>>();Map<String, Object> map1 new HashMap<String, Obj…

面试

Java简历与面试 置顶2017年10月20日 10:04:29 阅读数&#xff1a;40142 Java就业指导 想要成为合格的Java程序员或工程师到底需要具备哪些专业技能&#xff0c;面试者在面试之前到底需要准备哪些东西呢&#xff1f;本文陈列的这些内容既可以作为个人简历中的内容&#xff0c…

全文搜索引擎 Elasticsearch 入门教程

转载自 全文搜索引擎 Elasticsearch 入门教程全文搜索属于最常见的需求&#xff0c;开源的 Elasticsearch &#xff08;以下简称 Elastic&#xff09;是目前全文搜索引擎的首选。 它可以快速地储存、搜索和分析海量数据。维基百科、Stack Overflow、Github 都采用它。Elastic…

写给实习生的第一天

实习生&#xff08;intern&#xff09;和新员工有所区别。实习生仿佛一个长达12周&#xff08;三个月&#xff09;的面试&#xff0c;一起工作&#xff0c;一起解决问题。在最后有答辩和debrief meeting讨论结果。可能通过了&#xff0c;最后公司给offer&#xff1b;也可能没有…

项目里远程调用POST/GET接口时使用的方法

一、远程调用post接口时的方法 方法一&#xff1a; 这个方式的querys传的是参数&#xff08;参数可是单个参数/对象&#xff09; host指的是远程调用的主机地址 headers指的是头信息&#xff08;可有可无&#xff09; body指的是字符集的编码格式 public static String doPo…

再讲IQueryablelt;Tgt;,揭开表达式树的神秘面纱

接上篇《先说IEnumerable&#xff0c;我们每天用的foreach你真的懂它吗&#xff1f;》 最近园子里定制自己的orm那是一个风生水起&#xff0c;感觉不整个自己的orm都不好意思继续混博客园了&#xff08;开个玩笑&#xff09;。那么在此之前我们有必要仔细了解下 IQueryable<…

Java进阶之路——从初级程序员到架构师,从小工到专家

转载自 Java进阶之路——从初级程序员到架构师&#xff0c;从小工到专家怎样学习才能从一名Java初级程序员成长为一名合格的架构师&#xff0c;或者说一名合格的架构师应该有怎样的技术知识体系&#xff0c;这是不仅一个刚刚踏入职场的初级程序员也是工作三五年之后开始迷茫…

堆排序原理及其实现(C++)

https://blog.csdn.net/lzuacm/article/details/52853194 堆排序原理及其实现(C) 1. 堆排序的引入 我们知道简单选择排序的时间复杂度为O(n^2)&#xff0c;熟悉各种排序算法的朋友都知道&#xff0c;这个时间复杂度是很大的&#xff0c;所以怎样减小简单选择排序的时间复杂度…

Redis集群~StackExchange.redis连接Twemproxy代理服务器

本文是Redis集群系列的一篇文章&#xff0c;主要介绍使用StackExchange.Redis进行Twemproxy&#xff08;文中简称TW&#xff09;代理服务的连接过程&#xff0c;事务上&#xff0c;对于&#xff34;&#xff37;来说&#xff0c;我们需要理解一下它的物理架构&#xff0c;它类似…