【鉴权】OAuth 2.0: 高度灵活与安全的身份认证框架

目录

  • 引言
  • 一、OAuth 2.0 的核心概念
    • 1.1 资源拥有者(Resource Owner)
    • 1.2 客户端(Client)
    • 1.3 授权服务器(Authorization Server)
    • 1.4 资源服务器(Resource Server)
    • 1.5 OAuth 2.0体系架构图
  • 二、OAuth 2.0 授权流程
    • 2.1 OAuth 2.0 授权流程概述
    • 2.2 常见的授权模式
      • 2.2.1 授权码模式(Authorization Code Grant)
      • 2.2.2 隐式授权模式(Implicit Grant)
      • 2.2.3 资源所有者密码凭证模式(Resource Owner Password Credentials Grant)
      • 2.2.4 客户端凭证模式(Client Credentials Grant)
  • 三、访问令牌与刷新令牌
    • 3.1 访问令牌(Access Token)
    • 3.2 刷新令牌(Refresh Token)
  • 四、OAuth 2.0 授权流程图
  • 五、OAuth 2.0 的优势与广泛应用
    • 5.1 增强的安全性
    • 5.2 灵活性与可定制性
    • 5.3 跨平台的授权能力
    • 5.4 提升用户体验
    • 5.5 简化的权限管理
    • 5.6 广泛的应用场景
  • 总结
  • 参考资料

引言

OAuth 2.0不仅允许第三方应用在不访问用户密码的前提下安全地访问用户在其他平台上的资源(如社交媒体账户或云存储),还通过灵活的授权模式满足了不同应用场景的需求。无论是Web应用、移动端应用还是桌面客户端,OAuth 2.0都为开发者提供了一个高效、可靠的解决方案。在本篇文章中,我们将深入分析OAuth 2.0的核心概念、授权流程、常见授权模式以及其在实际开发中的应用,帮助开发者全面理解这一授权机制的优势与实现方法。

一、OAuth 2.0 的核心概念

OAuth 2.0 是一种授权框架,核心思想是 授权 而非 认证,其工作原理基于用户授权(而非认证)和令牌交换。它允许资源所有者授权客户端应用访问其在授权服务器上的资源,而无需将用户的凭证(如密码)暴露给客户端应用。这种方式不仅提升了用户体验,也增强了应用的安全性。

OAuth 2.0 主要涉及以下角色:

  • 资源所有者:通常是应用的用户,拥有受保护资源的访问权限。
  • 资源服务器:存储用户资源(如个人数据、文件等)的服务器,通常是 API 服务端。
  • 授权服务器:负责验证用户身份并颁发令牌的服务器。它为客户端应用提供访问资源服务器的授权。
  • 客户端应用:请求访问受保护资源的应用,可能是 Web 应用、移动应用、桌面客户端等。

1.1 资源拥有者(Resource Owner)

资源拥有者是 OAuth 2.0 中的核心角色,通常指的是应用的最终用户。用户拥有需要保护的资源,并可以授权第三方应用来访问这些资源。资源拥有者通过授权机制控制第三方应用的权限,避免直接暴露其个人信息或凭证。

  • 示例:在社交媒体平台(如 Twitter、Facebook)上,用户授权第三方应用(如相册管理工具)访问自己的社交资料或照片。

1.2 客户端(Client)

客户端是代表资源拥有者向授权服务器请求访问令牌的应用程序。客户端通常是第三方应用,或者是服务间进行通信的系统。客户端在获得用户授权后,凭借授权获取访问令牌(Access Token),从而访问资源服务器上的受保护资源。

  • 示例:社交媒体应用(如 Facebook)允许第三方应用(如照片管理工具)访问用户的社交媒体资料。

1.3 授权服务器(Authorization Server)

授权服务器负责认证资源拥有者身份并颁发访问令牌。授权服务器通常由 OAuth 提供商(如 Google、Facebook)维护,并与资源服务器协作完成令牌的颁发和管理。它需要处理授权请求、验证客户端的合法性,并在资源拥有者授权的情况下,生成并返回访问令牌。

  • 示例:Google 的 OAuth 2.0 认证服务器负责处理用户授权请求,并为客户端应用颁发访问令牌。

1.4 资源服务器(Resource Server)

资源服务器存储资源拥有者的受保护资源。资源服务器的职责是验证来自客户端的访问请求,并在令牌有效的情况下,提供所请求的资源。资源服务器和授权服务器可能是同一实体,也可能是不同的服务系统。

  • 示例:社交平台的资源服务器存储用户的社交数据(如朋友圈、照片),客户端通过访问令牌向资源服务器请求这些数据。

1.5 OAuth 2.0体系架构图

授权
颁发令牌
提供资源
访问
CSDN @ 2136
资源拥有者
客户端
授权服务器
资源服务器
CSDN @ 2136

图中展示了 OAuth 2.0 中各个角色之间的关系,其中资源拥有者授权客户端访问资源,授权服务器颁发令牌,资源服务器提供受保护的资源。

二、OAuth 2.0 授权流程

OAuth 2.0 定义了多个授权流程(也叫授权模式),每种授权模式适用于不同的应用场景。常见的授权模式包括授权码模式、隐式授权模式、资源所有者密码凭证模式和客户端凭证模式。选择适当的授权模式对于保证系统安全性至关重要。

2.1 OAuth 2.0 授权流程概述

OAuth 2.0 的基本流程包括以下几个步骤:

  1. 用户授权:用户在授权服务器上登录并同意授权给客户端应用。
  2. 客户端请求授权码:客户端应用向授权服务器请求一个 授权码,通常是在用户同意授权后。
  3. 授权码交换令牌:客户端应用使用获得的授权码向授权服务器请求 访问令牌,以便可以访问资源服务器。
  4. 访问受保护资源:客户端应用通过携带访问令牌向资源服务器请求受保护的资源。

2.2 常见的授权模式

OAuth 2.0 提供了多种授权模式,适应不同的应用场景和安全需求。主要的授权模式有:

  1. 授权码模式(Authorization Code Flow)
    最常用的模式,适用于 Web 应用。它的安全性高,因为授权码通过浏览器重定向传输,令牌的交换发生在客户端与授权服务器之间。

    • 适用于需要长期访问权限的应用。
    • 客户端和授权服务器之间的通信使用 HTTPS,确保传输过程的安全。
  2. 隐式授权模式(Implicit Flow)
    适用于纯前端的 Web 应用(如单页面应用 SPA)。隐式模式省略了授权码的步骤,直接将令牌颁发给客户端。

    • 适用于访问令牌生命周期短的场景。
    • 因为令牌直接传递给客户端,所以存在一定的安全风险,主要用于不需要长期认证的应用。
  3. 资源所有者密码模式(Resource Owner Password Credentials Flow)
    在某些受信任的客户端应用中,用户提供用户名和密码,客户端应用直接向授权服务器请求访问令牌。该模式需要信任客户端应用。

    • 常用于移动应用或者桌面客户端。
    • 不推荐广泛使用,因为用户密码直接传递给客户端,可能增加安全风险。
  4. 客户端凭证模式(Client Credentials Flow)
    适用于服务对服务的授权场景。客户端应用直接使用自己的凭证(而不是用户的)来请求访问令牌。

    • 常用于后台服务或无用户交互的应用场景。
    • 适用于访问不依赖于用户身份的受保护资源。

2.2.1 授权码模式(Authorization Code Grant)

授权码模式是 OAuth 2.0 中最常见的授权方式,特别适用于 Web 应用。该模式需要客户端和授权服务器之间进行两次交互:一次用于获取授权码,另一次用于交换访问令牌。

流程

  1. 客户端将用户重定向到授权服务器,用户登录并授权后,授权服务器返回授权码。
  2. 客户端使用该授权码向授权服务器请求访问令牌。
  3. 授权服务器验证授权码后返回访问令牌,客户端可以使用该令牌访问资源服务器。

优势

  • 安全性较高,因为访问令牌交换过程发生在服务器端,不直接暴露给用户。
  • 适用于具有服务器端机密的应用(如 Web 应用)。

适用场景

  • Web 应用和具有服务器端机密的移动应用,尤其是涉及用户敏感信息的场景。
  • 适用于需要长期访问权限的应用。
  • 客户端和授权服务器之间的通信使用 HTTPS,确保传输过程的安全。

2.2.2 隐式授权模式(Implicit Grant)

隐式授权模式适用于纯前端的单页 Web 应用(如单页面应用 SPA)。在这种模式下,省略了授权码的步骤,客户端直接通过浏览器获得访问令牌,无需通过授权码进行中介。

流程

  1. 客户端通过浏览器请求授权服务器,用户授权后,令牌直接返回给客户端。
  2. 客户端使用令牌直接访问资源服务器。

优势

  • 授权过程较快,适用于快速获取令牌的场景。
  • 不需要后端服务器参与令牌交换过程。

适用场景

  • 适用于无后端服务器的纯前端单页应用(SPA),但由于令牌直接暴露在前端,其安全性较低。
  • 适用于访问令牌生命周期短的场景。
  • 因为令牌直接传递给客户端,所以存在一定的安全风险,主要用于不需要长期认证的应用。

2.2.3 资源所有者密码凭证模式(Resource Owner Password Credentials Grant)

该模式适用于信任度较高的客户端应用(如企业内部应用),在此模式下,用户直接向客户端提供其用户名和密码,客户端使用这些凭证获取访问令牌。

流程

  1. 资源拥有者提供用户名和密码给客户端。
  2. 客户端使用用户名和密码向授权服务器请求访问令牌。
  3. 授权服务器验证凭证后返回访问令牌。

优势

  • 实现简单、直接、快速。
  • 适用于高度信任的应用,如企业内部应用或合作伙伴应用。

适用场景

  • 不推荐用于公共应用,仅适合信任度较高的客户端应用(如内网应用或合作伙伴应用)。
  • 常用于移动应用或者桌面客户端。
  • 不推荐广泛使用,因为用户密码直接传递给客户端,可能增加安全风险。

2.2.4 客户端凭证模式(Client Credentials Grant)

客户端凭证模式适用于服务间认证。客户端通过其自身凭证(如客户端 ID 和密钥)向授权服务器申请访问令牌,而无需用户授权,常用于 API 服务间的认证。

流程

  1. 客户端使用其客户端 ID 和密钥向授权服务器请求访问令牌。
  2. 授权服务器验证凭证后返回访问令牌。
  3. 客户端使用访问令牌访问资源服务器。

优势

  • 适用于无用户交互的后台服务和 API 调用。
  • 适用于微服务架构中的认证。

适用场景

  • 后台服务对后台服务(如微服务架构中的认证)进行 API 调用通信。
  • 常用于后台服务或无用户交互的应用场景。
  • 适用于访问不依赖于用户身份的受保护资源。

三、访问令牌与刷新令牌

在 OAuth 2.0 中,访问令牌和刷新令牌是两种重要的认证凭证。访问令牌用于客户端访问受保护的资源,而刷新令牌用于获得新的访问令牌,特别是在访问令牌过期时。

3.1 访问令牌(Access Token)

访问令牌是 OAuth 2.0 的核心,它允许客户端访问受保护的资源。通常,访问令牌是短期有效的,过期后需要使用刷新令牌重新获取。

  • 令牌格式:JWT(JSON Web Token)是 OAuth 2.0 中常用的令牌格式,包含三部分:头部(Header)、载荷(Payload)、签名(Signature)。
  • 有效期:通常为 1 小时左右,但具体有效期由授权服务器设置。

3.2 刷新令牌(Refresh Token)

刷新令牌是用于获取新的访问令牌的凭证,它具有较长的有效期,通常用于保持用户长期会话。刷新令牌不同于访问令牌,它本身不直接用于访问资源,而是用于令牌交换过程。

  • 优势:刷新令牌可以避免用户频繁授权,提升用户体验。
  • 安全性:刷新令牌具有较长的有效期,因此需要妥善保护,避免被泄露。

四、OAuth 2.0 授权流程图

以下是OAuth 2.0 授权流程的示意图:

用户 客户端应用 授权服务器 资源服务器 CSDN @ 2136 访问客户端应用 请求授权码 用户授权同意 提交授权 返回授权码 使用授权码换取令牌 返回访问令牌 使用令牌访问资源 返回受保护资源 展示数据 CSDN @ 2136 用户 客户端应用 授权服务器 资源服务器

五、OAuth 2.0 的优势与广泛应用

随着互联网应用的快速发展和跨平台服务的普及,如何高效、安全地管理用户的身份和授权成为开发者和服务提供商的一大挑战。OAuth 2.0 作为现代身份验证和授权的标准,凭借其显著的优势,在各种场景中得到了广泛应用。下面我们将深入探讨 OAuth 2.0 的主要优势及其应用场景。

5.1 增强的安全性

OAuth 2.0 的核心优势之一是它显著提升了安全性。在传统的认证机制中,用户的用户名和密码往往直接暴露在客户端和服务端之间,这使得密码泄露的风险增大。而在 OAuth 2.0 中,授权是通过令牌(Token)来完成的,避免了直接传输用户名和密码。这些令牌通常具有短期有效期,且可以被撤销或更新,进一步降低了潜在的安全隐患。

例如,如果某个服务的访问令牌被泄露,服务提供方可以迅速废除令牌,防止攻击者滥用访问权限。令牌不仅增强了安全性,还有效隔离了用户密码与其他服务的关系,从而减少了可能的攻击面。

5.2 灵活性与可定制性

OAuth 2.0 提供了多种授权模式,能够根据不同的应用场景灵活选择,满足从企业级应用到个人小型应用的各种需求。常见的授权模式包括:

  • 授权码模式(Authorization Code Grant):适用于 Web 应用和移动应用,在保证安全性的同时,还支持复杂的交互式用户认证。
  • 隐式授权模式(Implicit Grant):适用于 JavaScript 前端应用,直接在浏览器中获取访问令牌,适用于无服务器端支持的场景。
  • 密码模式(Password Credentials Grant):适合信任度较高的客户端应用,用户直接通过用户名和密码授权应用。
  • 客户端凭证模式(Client Credentials Grant):适用于机器对机器(M2M)的认证场景,不涉及用户身份验证,仅用于服务间的授权。

不同的应用可以根据自身的需求选择最适合的授权模式,从而提供更优的灵活性和定制化体验。

5.3 跨平台的授权能力

OAuth 2.0 使得跨平台授权成为可能,用户无需为每个应用单独创建账号和密码,只需通过已有的身份提供商(如 Google、Facebook、GitHub 等)进行一次授权,便可在多个平台间共享授权信息。这样的跨平台授权大大提升了用户体验。

例如,当你在一个网站上使用 Google 账户进行登录时,实际上是通过 OAuth 2.0 协议授权该网站访问你的 Google 账户信息,而无需直接输入密码。这种方式不仅简化了用户的登录流程,还使得跨平台和跨服务的应用能够更加无缝地协同工作。

此外,OAuth 2.0 还为开发者提供了与其他第三方应用(如社交媒体、支付平台等)进行集成的便利,极大地拓宽了应用的功能范围。

5.4 提升用户体验

OAuth 2.0 的另一个显著优势是对用户体验的提升。在传统的认证方式中,用户需要记住多个账号和密码,且往往要在不同的应用中重复输入这些信息。而使用 OAuth 2.0 后,用户只需在首次登录时授权一次,后续在支持 OAuth 的任何应用中都能够免去重新登录的烦恼。

这一点尤其在移动应用和单页面应用(SPA)中表现得尤为突出,用户可以更便捷地在不同应用间切换,无需再次验证身份,减少了登录过程的复杂度和时间消耗,提升了整体的用户体验。

5.5 简化的权限管理

OAuth 2.0 的令牌机制也简化了权限管理的工作。用户可以精确控制哪些应用可以访问其哪些资源。例如,某个第三方应用可能只需要用户的基本个人信息,OAuth 2.0 允许用户在授权时明确指定所需的权限,而不需要提供过多的个人信息。通过这种方式,OAuth 2.0 为用户提供了更大的隐私控制权,同时确保授权的服务只获取必要的信息。

5.6 广泛的应用场景

OAuth 2.0 在多个领域的应用场景中都展现了其强大的功能。它不仅被用于传统的 Web 应用中,还广泛应用于移动设备、IoT 设备以及云计算等新兴领域。以下是一些典型的应用场景:

  • 社交登录:例如,使用 Facebook、Google 或 Twitter 登录第三方网站或应用,简化用户登录流程。
  • 企业级 API 集成:公司可以通过 OAuth 2.0 授权,允许不同服务间安全地访问和共享数据,避免直接暴露用户的敏感信息。
  • 跨平台数据访问:用户可以授权一个应用访问另一个应用的数据,如同意 Google 地图访问用户的 Google 日历数据。
  • 移动和单页面应用:通过 OAuth 2.0,移动应用和 SPA 可以在无需存储用户密码的情况下,安全地访问服务器资源。

总结

OAuth 2.0 提供了一种安全、高效、可扩展的认证和授权框架,能够支持多种不同的授权模式,满足不同应用场景的需求。从资源拥有者、客户端到授权服务器和资源服务器,每个角色的分工清晰,保证了数据访问的安全性与隐私保护。通过合理使用访问令牌和刷新令牌,OAuth 2.0 在简化用户授权的同时,确保了系统的安全性与可扩展性。无论是提升安全性、简化权限管理,还是跨平台授权能力,OAuth 2.0 都能为开发者提供强大的支持,使其成为现代应用开发中不可或缺的重要工具。

参考资料

  • OAuth 2.0规范
  • OAuth 2.0实践教程

希望这篇详细的文章能够帮助你更好地理解 OAuth 2.0 的工作原理和实际应用!


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

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

相关文章

IP可用端口扫描器工具(bun + typescript)

IP可用端口扫描器工具(bun typescript) 学习方式:源码学习。通过项目和源码可以学习到如下内容:1、bun搭建项目,打包项目2、net、dns等node内置模块的使用3、yargs、assert、progress、cli-color等三方包的使用ps&am…

docker镜像仓库常用命令

docker镜像仓库常用命令 docker logindocker logoutdocker pulldocker pushdocker searchdocker imagesdocker image inspectdocker tagdocker rmidocker image prunedocker savedocker loaddocker history docker login 语法: docker login [options] [server] 功能&#xff…

软件开发项目管理:实现目标的实用指南

由于软件项目多数是复杂且难以预测的,对软件开发生命周期的深入了解、合适的框架以及强大的工作管理平台是必不可少的。项目管理系统在软件开发中通常以监督为首要任务,但优秀的项目计划、管理框架和软件工具可以使整个团队受益。 软件开发项目管理的主要…

外包干了2年,快要废了。。。

先说一下自己的情况,普通本科,在外包干了2年多的功能测试,这几年因为大环境不好,我整个人心惊胆战的,怕自己卷铺盖走人了,我感觉自己不能够在这样蹉跎下去了,长时间呆在一个舒适的环境真的会让一…

【青牛科技】GC8549替代LV8549/ONSEMI在摇头机、舞台灯、打印机和白色家电等产品上的应用分析

引言 在现代电子产品中,控制芯片的性能直接影响到设备的功能和用户体验。摇头机、舞台灯、打印机和白色家电等领域对控制精度、功耗和成本等方面的要求日益提高。LV8549/ONSEMI等国际品牌的芯片曾是这些产品的主要选择,但随着国内半导体技术的进步&…

Spring挖掘:(AOP篇)

学习AOP时,我们首先来了解一下何为AOP 一. 概念 AOP(面向切面编程,Aspect Oriented Programming)是一种编程技术,旨在通过预编译方式或运行期动态代理实现程序功能的统一管理和增强。AOP的主要目标是在不改变原有业务逻辑代码的…

Centos Linux 7 搭建邮件服务器(postfix + dovecot)

准备工作 1. 一台公网服务器(需要不被服务商限制发件收件的,也就是端口25、110、143、465、587、993、995不被限制),如有防火墙或安全组需要把这些端口开放 2. 一个域名,最好是com cn org的一级域名 3. 域名备案&am…

深入了解Bootstrap框架:从入门到精通

文章目录 前言Bootstrap的核心特性1. 响应式设计2. 丰富的组件库3. 易于使用4. 良好的兼容性 安装与使用安装1. 通过CDN引入2. 下载源码3. 使用npm或yarn 基本使用1. 栅格系统2. 按钮3. 导航条4. 卡片5. 模态框6. 轮播图7. 表单 高级定制1. 修改 Sass 变量2. 按需引入组件 最佳…

ENSP RIP动态路由

RIP(距离矢量路由协议)以网络中所有链路的距离和矢量为依据计算最佳路径,是第一个动态路由协议。条数作为唯一的度量单位。默认开启水平分割(从一个路由接口学到的路由信息,便不在从这个接口发送出去)防止路…

华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目——共8套(每套四十题)

华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目分享——共九套(每套四十题) 岗位——芯片与器件设计工程师 岗位意向——模拟芯片 真题题目分享,完整题目,无答案(共8套) 实习岗位…

MySQL45讲 第十一讲 怎么给字符串字段加索引?

文章目录 MySQL45讲 第十一讲 怎么给字符串字段加索引?一、引言二、前缀索引(一)概念与创建方式(二)数据结构与存储差异(三)确定前缀长度的方法 三、前缀索引对覆盖索引的影响四、其他索引创建方…

字节青训-小S的倒排索引

问题描述 小S正在帮助她的朋友们建立一个搜索引擎。为了让用户能够更快地找到他们感兴趣的帖子,小S决定使用倒排索引。倒排索引的工作原理是:每个单词都会关联一个帖子ID的列表,这些帖子包含该单词,且ID按从小到大的顺序排列。 例…

讲讲分布式与集群的区别?

大家好,我是锋哥。今天分享关于【讲讲分布式与集群的区别?】面试题。希望对大家有帮助; 讲讲分布式与集群的区别? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在现代计算和信息技术领域,分布式系统和集…

大数据新视界 -- 大数据大厂之 Impala 性能优化:解锁大数据分析的速度密码(上)(1/30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

大数据新视界 -- 大数据大厂之 Impala 性能优化:数据存储分区的艺术与实践(下)(2/30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

【实用教程】Blazor 文件管理器中引入分页功能

分页是一项重要功能,可帮助我们有效地加载大量数据。我们的 Syncfusion Blazor 文件管理器允许在分段页面中显示文件和文件夹,从而更轻松地浏览大型目录。在文件管理器组件中处理大量数据时,此功能非常方便。此功能可用于有效地加载大量数据。…

C++上机实验|多态性编程练习

1.实验目的 (1)理解多态性的概念。 (2)掌握如何用虚函数实现动态联编 (3)掌握如何利用虚基类。 2.实验内容 设计一个飞机类 plane,由它派生出歼击机类fighter和轰炸机类 bomber,歼击机类fighter 和轰炸机类bomber 又共同派生出歼轰机(多用途战斗机)。利用虚函数和虚基类描述…

CSS弹性布局:灵活布局的终极指南

在网页设计中,CSS 弹性布局(Flexbox)是一个不可或缺的工具。它能帮助你轻松地排列和对齐元素,尤其是在响应式设计中表现出色。今天,我们就来深入探讨一下 Flexbox 的各个属性,让你彻底掌握这个强大的布局工…

Java:二维数组

目录 1. 二维数组的基础格式 1.1 二维数组变量的创建 —— 3种形式 1.2 二维数组的初始化 \1 动态初始化 \2 静态初始化 2. 二维数组的大小 和 内存分配 3. 二维数组的不规则初始化 4. 遍历二维数组 4.1 for循环 ​编辑 4.2 for-each循环 5. 二维数组 与 方法 5.1…

SQL,力扣题目1767,寻找没有被执行的任务对【递归】

一、力扣链接 LeetCode_1767 二、题目描述 表:Tasks ------------------------- | Column Name | Type | ------------------------- | task_id | int | | subtasks_count | int | ------------------------- task_id 具有唯一值的列。 ta…