在探讨前端常用的鉴权方式之前,我们先来明晰几个关键概念:认证、授权、鉴权以及权限控制,搞清楚它们之间的关系,能帮助我们更好地理解后续内容。
认证(Identification),是依据声明者独有的识别信息来确认其身份。就好比我们需要用身份证来证明“我就是我”。常见的认证技术涵盖用户的生物学特征,如指纹、语音、眼睛虹膜,还有用户的大数据识别等。
授权(Authorization),在信息安全领域指资源所有者委派执行者,并赋予执行者特定范围的资源操作权限。在现实生活中,像银行卡(由银行派发)、门禁卡(由物业管理处派发)、钥匙(由房东派发),这些都是授权的体现;在互联网领域,web服务器的session机制、web浏览器的cookie机制、颁发授权令牌(token)等,均属于授权机制。
鉴权(Authentication),在信息安全领域,是对声明者所声明的身份权利的真实性进行鉴别确认。从授权的角度出发,更容易理解鉴权,授权和鉴权是上下游匹配的关系,先有授权,才有鉴权。在现实生活里,门禁卡需要通过门禁卡识别器验证,银行卡需要通过银行卡识别器验证;在互联网领域,则是校验session/cookie/token的合法性与有效性。鉴权起着承上启下的作用,它接收授权的输出,校验其真实性后获取权限(permission),为下一步的权限控制做准备。
权限控制(Access/Permission Control),是将可执行的操作定义为权限列表,然后判断操作是否被允许或禁止。可以从权限和控制两方面来理解,权限是抽象的逻辑概念,控制是具体的实现方式。例如在现实生活中,有的门禁卡拥有开公司所有门的权限,有的门禁卡因具备管理员角色权限,所以能开公司所有的门;在互联网领域,通过web后端服务控制接口访问,决定是否允许访问请求。
认证、授权、鉴权和权限控制这四个环节,通常是前后依次发生、呈上下游关系的。不过,在某些场景下它们也会同时出现。比如使用门禁卡开门,认证、授权、鉴权、权限控制四个环节瞬间同时完成;用户登录网站时,在使用用户名和密码登录的那一刻,认证和授权一同完成,而鉴权和权限控制则在后续的请求访问中,如选购物品或支付时发生。
接下来,我们详细探讨前端常用的鉴权方式。
HTTP基本鉴权
在HTTP中,基本认证方案(Basic Access Authentication)允许客户端(一般指网页浏览器)在请求时,通过用户提供用户名和密码的方式来验证用户身份。不过,如今几乎所有的线上网站都不会采用该认证方案,了解即可。
认证流程图及步骤解析:
- 客户端(如浏览器)向服务器请求受限的列表数据或资源,例如:
GET /list/ HTTP/1.1
Host: www.baidu.com
Authorization: Basic aHR0cHdhdGNoOmY=
- 服务器告知客户端该资源在安全区(如baidu.com)内,属于受限资源,需要进行基本认证,并向客户端返回401状态码(Unauthorized未被授权的),同时附带一个认证域“www - Authenticate: Basic realm = ”baidu.com””要求进行身份验证,其中“Basic”是验证模式,“realm = "baidu.com"”表明客户端需输入该安全域的用户名和密码,而非其他域的,如下所示:
HTTP/1.1 401 Unauthorized
www - Authenticate: Basic realm = "baidu.com"
- 客户端收到要求后,若为浏览器,会自动弹出一个弹窗让用户输入用户名和密码。输入完毕后,客户端将用户名及密码以Base64加密方式发送给服务器,传送格式如下(其中Basic内容为:用户名:密码的ase64形式):
GET /list/ HTTP/1.1
Authorization: Basic Ksid2FuZzp3YW5n==
- 服务器校验“Authorization”字段中的用户名和密码,若正确,则返回客户端所需资源:
HTTP/1.1 OK...
优点:该方式较为简单,且基本所有流行的浏览器都支持。
缺点:
- 不安全:基于HTTP传输,数据几乎是裸奔的。虽使用Base64编码,但很容易被解码。即便认证内容无法解码为原始用户名和密码,恶意用户获取认证内容后,也可利用其不断向服务器发起请求,即重放攻击。
- 无法主动注销:HTTP协议未提供清除浏览器中Basic认证信息的机制,除非关闭标签页或浏览器、清除历史记录。
使用场景:适用于内部网络,或者对安全要求不高的网络。
Session - Cookie鉴权
Session - Cookie认证利用服务端的Session(会话)和浏览器(客户端)的Cookie来实现前后端通信认证。在理解这种方式前,我们先简单了解一下Cookie和Session。
HTTP是无状态协议,对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息。为了让服务器区分不同的客户端,就需要主动维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器,而这个状态可以通过Cookie来实现。Cookie是由服务器生成,发送给浏览器并存储在浏览器中的一小段数据,下次浏览器向同一服务器发送请求时,会自动带上该Cookie。
Session则是服务器端为每个客户端会话创建的一个对象,用于存储与该客户端相关的信息。可以说Session本质上也是一种特殊的Cookie,只不过它是包含会话ID的Cookie。
鉴权步骤如下:
- 用户在浏览器将用户凭证(如通过表单提交的账号密码)发送到服务器。
- 服务器认证通过后,在服务器内部存储一个包含有效期的会话ID,同时把该会话ID包装成Cookie发送给浏览器。
- 浏览器存储该Session,并且每次请求网站时都会携带该Session。
- 服务器收到请求后,能成功匹配发来的Session会话ID,则表示认证成功。
优点:
- 广泛支持:几乎所有的浏览器都支持Cookie和Session机制,兼容性好。
- 简单易用:实现相对简单,对于一些小型应用或者对安全性要求不是特别高的场景,是一种较为常用的鉴权方式。
缺点:
- 易受CSRF攻击:基于cookie的一种跨站伪造攻击。因为基于cookie来识别用户,用户本身携带cookie值,一旦cookie被截获,用户就容易被伪造身份进行非法操作。
- 跨域问题:Cookie的发送由浏览器自动管理,浏览器和服务器规定了一系列对Cookie发送的限制,使得Cookie在跨域使用时非常麻烦。例如SameSite属性默认限制跨站点发送;Domain和Path绑定到特定域名和路径;Secure和HttpOnly受协议和客户端行为影响。
使用场景:适用于同域下的Web应用,对安全性要求不是极高,且不需要频繁进行跨域操作的场景。
Token验证
Token是用户身份的一种验证方式,通常被称为“令牌”。当用户首次登录后,服务器生成一个Token并返回给客户端,此后客户端每次请求数据时,只需带上这个Token,无需再次携带用户名和密码。
最简单的Token组成包含:
- uid:用户唯一的身份标识。
- time:当前时间的时间戳,可用于验证Token的时效性。
- sign:签名,由Token的前几位 + 盐以哈希算法压缩成一定长度的十六进制字符串,用于防止恶意第三方拼接Token请求服务器。
也可以把一些不变的参数放进Token,减少查库次数。
验证流程如下:
- 客户端使用用户名跟密码请求登录。
- 服务端收到请求,验证用户名与密码。
- 验证成功后,服务端签发一个Token,并发送给客户端。
- 客户端收到Token后,可将其存储起来,比如放在Cookie里或者Local Storage里。
- 客户端每次向服务端请求资源时,需带上服务端签发的Token。
- 服务端收到请求后,验证客户端请求中携带的Token。若验证成功,返回客户端请求的数据。
优点:
- 无状态:服务端无需保留用户的认证信息或者会话信息,基于Token认证机制的应用无需考虑用户在哪一台服务器登录,解决了Session扩展性的弊端。
- 时效性:相比session - cookie中固定不变的sessionid,Token可以在一段时间内动态改变,安全性更高。
- 可扩展性:服务端不保存Token信息,在分布式系统中,各个节点只需验证Token的真实性,具有更好的扩展性。
缺点:
- 占带宽:正常情况下Token比session_id更大,需要消耗更多流量,挤占更多带宽,不过在实际应用中,这种影响通常可忽略不计。
- 性能问题:相比于session - cookie,服务端验证Token需要花费更多时间和性能进行解密验证,可看作是一种“时间换空间”的方案。
使用场景:广泛应用于前后端分离架构、分布式系统以及对安全性和扩展性要求较高的应用中。例如,许多移动应用的后端服务采用Token鉴权,方便客户端在不同设备、不同网络环境下进行访问。
JWT(JSON Web Tokens)
JWT是一种特殊的Token,属于一种开放标准(RFC 7519),用于在网络应用中安全地传输信息。当服务器认证用户后,会生成一个JSON对象并发送给用户。之后,用户与服务器通信时,服务器完全依靠这个对象来认定用户身份。为防止用户篡改数据,服务器生成该对象时会加上签名。
JWT的数据结构:
JWT是一个很长的字符串,中间用点(.)分隔成三个部分,分别是Header(头部)、Payload(负载)、Signature(签名)。
- Header:是一个JSON对象,描述JWT的元数据。例如:
{ "alg": "HS256", "typ": "JWT" }
其中,alg属性表示签名的算法,默认是HMAC SHA256(写成HS256);typ属性表示这个令牌(token)的类型(type),JWT令牌统一写为JWT。头部的JSON对象会使用Base64URL算法转成字符串。
- Payload:也是一个JSON对象,用于存放实际需要传递的数据。同样,这个JSON对象也要使用Base64URL算法转成字符串。需要注意的是,JWT默认不加密,任何人都能读取,所以不要把敏感信息放在这个部分。
- Signature:是对前两部分的签名,用于防止数据篡改。签名的生成需要指定一个密钥(secret),然后使用Header里面指定的签名算法(默认是HMAC SHA256)。例如:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
优点:
- 无状态:服务器无需保存任何session数据,变成无状态的,更容易实现扩展,在分布式系统中优势明显。
- 跨语言支持:由于JWT是基于JSON格式的,几乎所有的编程语言都能很好地支持和处理JSON,方便在不同语言开发的前后端之间传递和验证。
缺点:
- 不可控性:JWT一旦签发,就不再受服务端控制,因为它在服务端没有记录。这既是它最大的优点,也是最大的缺点,可能会导致一定的不可控性。例如,若JWT泄露,在其过期之前,恶意用户都可以使用它访问受保护资源。
使用场景:常用于前后端分离的Web应用、单页应用(SPA)以及移动应用的身份验证和授权场景,尤其是在分布式系统和微服务架构中,因其无状态和易于扩展的特性而备受青睐。
单点登录(SSO - Single Sign - On)
单点登录允许用户只需登录一次,即可访问所有相互信任的应用系统。通常,SSO需要一个独立的认证中心(passport),子系统的登录均通过passport完成,子系统本身不参与登录操作。当一个系统成功登录后,passport会颁发一个令牌给各个子系统,子系统可凭借该令牌获取各自的受保护资源。为减少频繁认证,各个子系统在被passport授权后,会建立一个局部会话,在一定时间内无需再次向passport发起认证。
实现流程示例:
- 用户访问子系统A,A系统检测到用户未登录,重定向用户到认证中心(passport)。
- 用户在认证中心输入用户名和密码进行登录。
- 认证中心验证用户信息成功后,生成一个SSO - Token(或Ticket),并将其返回给子系统A,同时为用户在认证中心建立全局会话。
- 子系统A收到SSO - Token后,存储该Token,并建立局部会话,同时将用户重定向回原本请求的页面。
- 当用户访问另一个子系统B时,B系统检测到用户未登录,同样重定向用户到认证中心。
- 认证中心检测到用户已登录,且持有有效的SSO - Token,直接将该Token发送给子系统B。
- 子系统B收到Token后,验证其有效性,若有效,则建立局部会话,允许用户访问。
优点:
- 提升用户体验:用户无需在多个应用系统中重复登录,大大提高了使用效率和便捷性。
- 集中管理:认证中心统一管理用户的登录信息,便于进行用户身份验证和权限管理,降低了管理成本。
缺点:
- 复杂度增加:需要搭建和维护独立的认证中心,涉及多个系统之间的协作和交互,系统架构变得更加复杂。
- 安全风险:一旦认证中心出现安全问题,如被攻击导致用户信息泄露,将会影响到所有依赖该认证中心的子系统。
使用场景:适用于企业内部多个相互关联的应用系统之间,或者大型互联网平台下的多个子业务系统之间,实现统一的用户登录和权限管理,提升用户体验和管理效率。例如,大型企业的办公系统,员工通过一次登录即可访问邮件系统、OA系统、文件管理系统等多个内部应用。
OAuth(开放授权)
OAuth是目前最流行的授权机制,主要用于授权第三方应用获取用户数据。简单来说,OAuth就是一种授权机制,数据的所有者告知系统,同意授权第三方应用进入系统获取相关数据,系统会生成一个短期的进入令牌(token),第三方应用使用该令牌代替密码进行访问。
令牌与密码的差异:
- 有效期:令牌是短期的,到期会自动失效,用户无法自行修改;而密码一般长期有效,除非用户主动修改。
- 可撤销性:令牌可以被数据所有者随时撤销,撤销后立即失效;密码一般不允许被他人撤销。
- 权限范围:令牌有权限范围(scope),例如只能访问特定的资源或执行特定的操作;而密码通常具有完全的访问权限。
OAuth 2.0的授权流程(以常见的授权码模式为例):
- 第三方应用(客户端)向资源所有者(用户)请求授权,并提供一个回调URL。
- 资源所有者同意授权后,用户代理(通常是浏览器)将用户重定向到认证服务器。
- 认证服务器验证用户身份后,向用户展示授权页面,询问用户是否同意第三方应用获取其资源。
- 用户同意授权后,认证服务器生成一个授权码,并将用户重定向回第三方应用提供的回调URL,同时将授权码作为参数传递给第三方应用。
- 第三方应用收到授权码后,使用该授权码向认证服务器请求访问令牌。在请求过程中,第三方应用需要提供自己的客户端ID和客户端密钥进行身份验证。
- 认证服务器验证授权码和第三方应用的身份后,生成访问令牌(access token)和刷新令牌(refresh token),并将它们返回给第三方应用。
- 第三方应用使用访问令牌访问资源服务器上的受保护资源。访问令牌的有效期通常较短,当访问令牌过期后,第三方应用可以使用刷新令牌向认证服务器请求新的访问令牌。
优点:
- 安全:通过令牌代替密码,减少了密码泄露的风险。同时,令牌具有有效期和权限范围限制,进一步增强了安全性。
- 方便第三方应用接入:对于第三方应用开发者来说,OAuth 2.0提供了一种标准的授权方式,简化了接入流程,降低了开发成本。
缺点:
- 流程复杂:OAuth 2.0的授权流程涉及多个步骤和多个角色之间的交互,相对较为复杂,理解和实现起来有一定难度。
- 依赖认证服务器:整个授权过程依赖于认证服务器的可靠性和安全性,如果认证服务器出现故障或被攻击,可能会影响第三方应用的正常使用。
使用场景:广泛应用于需要第三方应用接入的场景,如社交媒体平台允许第三方应用获取用户的部分信息(如头像、昵称等),以实现更丰富的功能。在互联网应用中,常见的“使用微信登录”“使用QQ登录”等功能,背后大多采用了OAuth 2.0授权机制。
应用实例:Vue项目中使用路由守卫实现前端鉴权
在前后端分离的项目中,前端需要对用户的访问进行鉴权,以确保用户只能访问其有权限访问的页面。以Vue项目为例,我们可以使用路由守卫来实现前端鉴权。
什么是路由守卫:
路由守卫(Route Guard)是Vue.js中vue - router提供的一种功能,用于在路由切换时执行特定的逻辑,以控制和管理路由的访问。它类似于现实生活中的门卫,负责检查用户是否有权限进入某个区域(在这里指的是路由对应的页面或组件)。路由守卫可以在路由导航的不同阶段被调用,包括全局守卫、路由独享守卫和组件内守卫,通过它们可以实现诸如登录验证、权限控制、页面跳转控制等功能。
实现前端鉴权的步骤:
- **设置页面
前端开发,鉴权方式,Token,Session,Cookie,JWT,OAuth,OpenID Connect, 单点登录,权限管理,跨域认证,身份验证,API 安全,Bearer Token, 认证流程
资源地址:
https://pan.quark.cn/s/50438c9ee7c0