南昌高端网站建设采购网有哪些平台
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:18
当前位置: 首页 > news >正文
南昌高端网站建设,采购网有哪些平台,物联网平台开发,wordpress分页ajax本专栏将从基础开始#xff0c;循序渐进#xff0c;以实战为线索#xff0c;逐步深入SpringSecurity相关知识相关知识#xff0c;打造完整的SpringSecurity学习步骤#xff0c;提升工程化编码能力和思维能力#xff0c;写出高质量代码。希望大家都能够从中有所收获#… 本专栏将从基础开始循序渐进以实战为线索逐步深入SpringSecurity相关知识相关知识打造完整的SpringSecurity学习步骤提升工程化编码能力和思维能力写出高质量代码。希望大家都能够从中有所收获也请大家多多支持。 专栏地址:SpringSecurity专栏 本文涉及的代码都已放在gitee上gitee地址 如果文章知识点有错误的地方请指正大家一起学习一起进步。 专栏汇总专栏汇总 文章目录12.1 OAuth 2框架12.2 OAuth 2认证架构的组成部分12.3 OAuth 2的使用12.3.1 授权码授予类型的使用12.3.2 密码授予类型的使用12.3.3 客户凭证授予类型的使用12.3.4 使用刷新token来获得新的访问token12.4 OAuth 2的缺点12.5 实现一个简单的单点登录应用程序12.5.1 管理授权服务器12.5.2 开始实现12.5.3 实现客户注册12.5.4 实现ClientRegistrationRepository12.5.5 Spring Boot配置的魅力12.5.6 获取已验证用户的详细信息12.5.7 测试应用程序本章包括OAuth 2的概述OAuth 2规范的介绍建立一个使用单点登录的OAuth 2应用程序 如果你已经在使用OAuth 2我知道你在想什么OAuth 2框架是一个庞大的主题在四章中你会学到所有你需要知道的关于用Spring Security应用OAuth 2的知识。在这一章中我们将从概述开始你会发现OAuth 2框架的主要角色是用户、客户端、资源服务器和授权服务器。在总体介绍之后你将学习如何使用Spring Security来实现客户端。然后在第13章到第15章我们将讨论实现最后两个组件资源服务器和授权服务器。 为了达到这个目标在本章中我们将讨论什么是OAuth 2然后我们将把它应用于一个专注于单点登录SSO的认证的应用。我之所以喜欢用SSO的例子来讲授这个课题是因为它非常简单但也非常有用。它提供了一个关于OAuth 2的概述并让你在不写太多代码的情况下实现一个完全有效的应用程序从而获得满足感。 在第13章到第15章中我们将把本章所涉及的内容应用于你在本书前几章中已经熟悉的代码实例中。一旦我们完成这四章你就会对在你的应用程序中用Spring Security实现OAuth 2所需要的东西有一个很好的概念。 由于OAuth 2是一个很大的主题我将在适当的地方提到我认为必要的知识。 Spring Security让用OAuth 2开发应用程序变得很容易。你需要开始的唯一先决条件是本书的第2章到第11章在这些章节中你学到了Spring Security中认证和授权的一般架构。我们将讨论的关于OAuth 2的内容基于Spring Security的标准授权和认证架构。 12.1 OAuth 2框架 在本节中我们将讨论OAuth 2框架。今天OAuth 2被普遍用于保障网络应用的安全所以你可能已经听说过它了。你很有可能需要在你的应用程序中应用OAuth 2。这就是为什么我们需要讨论在Spring应用程序中使用Spring Security来应用OAuth 2。我们从一点理论开始然后在一个使用SSO的应用程序中应用它。 在大多数情况下OAuth 2被称为一个授权框架或规范框架其主要目的是允许第三方网站或应用程序访问资源。有时人们把OAuth 2称为一个委托协议。无论你选择如何称呼它重要的是要记住OAuth 2不是一个具体的实现或一个库。你也可以用其他平台、工具或语言来应用OAuth 2的流程定义。在本书中你会发现如何用Spring Boot和Spring Security实现OAuth 2。 我认为理解OAuth 2是什么以及它的用处的一个好方法是用我们在本书中已经分析过的例子开始讨论。到目前为止你在大量的例子中看到的最微不足道的认证方式是HTTP Basic认证方式。这对我们的系统来说还不够对于HTTP Basic认证我们有两个问题需要考虑到 为每一个请求发送凭证图12.1由一个单独的系统管理用户的凭证 图12.1 当使用HTTP基本认证时你需要在所有请求中发送凭证并重复认证逻辑。这种方法意味着经常在网络上分享凭证。 为每一个请求发送证书可能在个别情况下有效但这通常是不可取的因为它意味着 经常通过网络共享凭证让客户浏览器在网络应用的情况下以某种方式存储证书以便客户可以将这些证书与请求一起发送给服务器以获得认证和授权。 我们想从我们的应用程序的架构中摆脱这两点因为它们削弱了安全性。大多数情况下我们希望有一个单独的系统来管理用户凭证。想象一下你必须为你组织中的所有应用程序配置和使用单独的凭证图12.2。 图12.2 在一个组织中你与多个应用程序一起工作。其中大部分都需要你进行认证才能使用。对你来说知道多个密码是一种挑战对组织来说管理多套凭证也是一种挑战。 如果我们把凭证管理的责任隔离在系统的一个组件中那就更好了。我们暂且称它为授权服务器图12.3。 图12.3 一个更容易维护的架构将凭证分开保存并允许所有应用程序为其用户使用同一套凭证。 这种方法消除了代表同一个人的证书的重复性。通过这种方式架构变得更简单更容易维护。 12.2 OAuth 2认证架构的组成部分 在本节中我们将讨论在OAuth 2在认证中发挥作用的组件。你需要了解这些组件和它们所扮演的角色因为我们在接下来的章节中会提到它们。但在本节中我们只讨论这些组件是什么以及它们的用途图12.4。正如你将在第12.3节中所了解的这些组件之间有更多的交互方式。而且在那一节中你还会了解到导致这些组件之间不同交互的不同流程。 如前所述OAuth 2组件包括 资源服务器-托管用户拥有的资源的应用程序。 资源可以是用户的数据或他们的授权行为。用户也被称为资源所有者–拥有资源服务器所提供的资源的个人。一个用户通常有一个用户名和密码用来识别自己。客户端–代表用户访问其拥有的资源的应用程序。授权服务器–授权客户端访问资源服务器所提供的用户资源的应用程序。当授权服务器决定客户端被授权代表用户访问某项资源时它就会发出一个token。客户端使用这个token向资源服务器证明它是由授权服务器授权的。如果客户端有一个有效的token资源服务器就允许它访问它所请求的资源。 图12.4 OAuth 2架构的主要组成部分是资源所有者、客户端、授权服务器和资源服务器。每一个都有自己的职责这在认证和授权过程中是至关重要的。 12.3 OAuth 2的使用 在本节中我们将讨论如何应用OAuth 2这取决于你的应用程序的架构。正如你将了解到的OAuth 2意味着多种可能的认证流程你需要知道哪一种适用于你的情况。在本节中我将采取最常见的情况并对这些情况进行评估。重要的是在开始第一个实现之前要先做这件事以便你知道你在实现什么。 那么OAuth 2是如何工作的实现OAuth 2的认证和授权是什么意思简而言之OAuth 2是指使用token进行授权。 请记住11.2节token就像访问卡。一旦你获得一个token你就可以访问特定的资源。 但OAuth 2提供了多种获得token的可能性称为授予。下面是最常见的OAuth 2授权你可以选择。 授权码密码刷新token客户端凭证 12.3.1 授权码授予类型的使用 在本节中我们将讨论授权码授予类型图12.5。我们还将在第12.5节中实现的应用程序中使用它。该授予类型是最常用的OAuth 2流程之一所以了解它的工作原理和如何应用它是相当重要的。你很有可能会在你开发的应用程序中使用它。 图12.5 授权码授予类型。客户端要求用户直接与授权服务器交互为用户的请求授予许可。一旦获得授权授权服务器就会发出一个token客户端使用该token来访问用户的资源。 图12.5中的箭头不一定代表HTTP请求和响应。例如当客户端告诉用户图中顶部的第二个箭头“告诉授权服务器你允许我做这个动作”客户端就会把用户重定向到授权服务器的登录页面。当授权服务器给客户端一个token时授权服务器实际上是通过我们所说的重定向URI来调用客户端。 你将在第12章到第15章中学习所有这些细节所以现在不用担心。通过这个说明我想让你知道这些顺序图并不只是代表HTTP请求和响应。 这些是对OAuth 2行为体之间通信的简化描述。 以下是授权码授予类型的工作方式。在这之后我们深入了解每个步骤的细节。 提出认证请求获得一个访问token调用受保护的资源 第一步用授权码授予类型提出认证请求 客户端将用户重定向到授权服务器的一个api在那里他们需要进行认证。你可以想象你正在使用应用程序X你需要访问一个受保护的资源。为了访问该资源App X需要你进行身份验证。 它为你打开一个页面在授权服务器上有一个登录页你必须用你的证书来填写。 从技术上讲这里发生的情况是当客户端将用户重定向到授权服务器时客户端调用授权端点请求查询中包含以下细节: response_type的值为code它告诉授权服务器客户端希望有一个code。客户端需要这个code来获得一个访问token你将在第二步中看到。client_id其值为客户端ID用于识别应用程序本身。redirect_uri它告诉授权服务器在成功认证后将用户重定向到哪里。有时授权服务器已经知道每个客户的默认重定向URI。由于这个原因客户端不需要发送重定向URI。scope这与我们在第五章中讨论的授予权限相似。state其中定义了一个跨站请求伪造CSRFtoken用于我们在第10章讨论的CSRF保护。 认证成功后授权服务器重定向URI并提供一个code和state。客户端检查state是否与它在请求中发送的state相同以确认它不是其他人在试图调用重定向URI。客户端使用该code来获得步骤2中提出的访问token。 步骤2获得具有授权码授予类型的访问token 为了允许用户访问资源步骤1产生的code是客户端证明用户认证的证据。你猜得没错这就是为什么这被称为授权代码授予类型。现在客户端用code调用授权服务器以获得token。 图12.6 第一步意味着用户和授权服务器之间的直接互动。在这第二步中客户向授权服务器请求一个访问token并提供在第一步中获得的授权码。 在许多情况下这前两步会造成混乱。人们通常不明白为什么这个流程需要两次调用授权服务器和两个不同的token–授权码和访问token。花点时间来理解这一点。 授权服务器生成第一个code作为用户直接与之互动的证明。客户端收到这个code后必须用它和它的凭证再次进行认证以获得一个访问token。客户端使用第二个token来访问资源服务器上的资源。 那么为什么授权服务器没有直接返回第二个token访问token嗯OAuth 2定义了一个叫做隐式授予类型的流程授权服务器直接返回一个访问token。本节没有列举隐式授予类型因为不建议使用这种类型而且现在大多数授权服务器都不允许这样做。授权服务器直接用访问token调用重定向URI而不确定它确实是接收该token的正确客户端这个简单的事实使得流程的安全性降低。通过先发送一个授权码客户必须通过使用他们的证书来获得一个访问token来再次证明他们是谁。客户端进行最后一次调用以获得访问token并发送 授权码证明用户对其进行了授权他们的证书这证明他们确实是同一个客户而不是其他截获授权码的人 回到第2步从技术上讲客户端现在向授权服务器提出了一个请求。这个请求包含以下细节 code,这是在步骤1中收到的授权代码。这就证明了用户的身份验证。client_id 和 client_secret 客户的凭证。redirect_uri 这与步骤1中的一样。grant_type grant_type对应的值为authority_code它标识了所使用的流的种类。一个服务器可能支持多个认证流所以必须指定哪个是当前执行的认证流。 作为响应服务器会发回一个access_token。这个token是一个值客户可以用它来调用资源服务器所暴露的资源。 第3步用授权码授予类型调用受保护资源 在成功地从授权服务器获得访问token后客户端现在可以调用受保护的资源了。客户端在调用资源服务器的api时在授权请求头中使用访问token。 在本节的最后我用一个比喻来说明这种流动。我有时会从一家我认识很久的小店买书。我必须提前订书然后在几天后取书。但这家店不在我的日常上下班路线上所以有时我不能自己去取书。我通常会请住在我附近的朋友去那里帮我取书。当我的朋友询问我的订单时商店的女士会打电话给我确认我已经派人去取书了。在我确认后我的朋友就会去取包裹并在当天晚些时候把它带给我。 在这个比喻中书籍是资源。我拥有它们所以我是用户资源所有者。为我取书的朋友是client。卖书的女士是授权服务器。(我们也可以把她或书店看作是资源服务器。)请注意为了授予我的朋友客户取书资源的许可卖书的女士授权服务器直接给我用户打电话。这个类比描述了授权代码和隐式授予类型的过程。当然由于我们在故事中没有token这个类比是局部的描述了两种情况。 授权码授予类型有一个很大的优点就是使用户能够允许客户端执行特定的动作而不需要与客户端分享他们的凭证。但这种授予类型有一个弱点如果有人拦截了授权代码会发生什么当然客户端需要用它的凭证进行验证正如我们之前讨论的那样。但是如果客户端的凭证也被盗了呢即使这种情况不容易实现我们也可以认为它是这种授予类型的漏洞。 12.3.2 密码授予类型的使用 在本节中我们讨论密码授予类型图12.7。这种授予类型也被称为资源所有者凭证授予类型。使用这种流程的应用程序假定客户端收集用户凭证并使用这些凭证来验证和从授权服务器获得访问token。 还记得我们在第11章中的实战例子吗我们实现的架构与密码授予类型中的情况非常接近。在第13章到第15章我们还用Spring Security实现了一个真正的OAuth 2密码授予类型架构。 这时你可能会问资源服务器是如何知道一个token是否有效的。在第13章和第14章我们将讨论资源服务器用来验证token的方法。目前你应该把注意力集中在授予类型的讨论上因为我们只提到了授权服务器如何发放访问token。 图12.7 密码授予类型假定用户与客户端共享他们的凭证。 客户端使用这些凭证从授权服务器获得一个token。然后它代表用户从资源服务器上访问资源。 只有当客户端和授权服务器是由同一个组织建立和维护时你才会使用这个认证流程。为什么呢让我们假设你建立了一个微服务系统你决定将认证责任分离为一个不同的微服务以提高可扩展性并保持每个服务的责任分离。 这种分离在许多系统中被广泛使用。 让我们进一步假设你的系统的用户使用的是一个用Angular、ReactJS或Vue.js等前端框架开发的客户端Web应用或者他们使用的是一个移动应用程序。在这种情况下用户可能会认为从你的系统被重定向到同一个系统进行认证然后再返回是很奇怪的事情。这就是像授权码授予类型的流程会发生的情况。对于密码授予类型你会期望应用程序向用户提供一个登录表单并让客户端负责向服务器发送凭证来进行认证。用户不需要知道你是如何在你的应用程序中设计认证责任的。让我们看看在使用密码授予类型时会发生什么。 这两个任务如下。 请求一个访问token。使用访问token来调用资源。 第1步使用密码授予类型时请求访问token 密码授予类型的流程要简单得多。客户端收集用户凭证并调用授权服务器以获得一个访问token。当请求获得访问token时客户端还在请求中发送以下细节。 grant_type值为passwordclient_id 和 client_secret是客户用来认证自己的凭证。scope它代表了被授予的权力username 和password 是用户凭证。这些是以纯文本形式作为请求头的值发送的。 客户端在响应中收到一个访问token。客户端现在可以使用该访问token来调用资源服务器的端点。 第2步使用密码授予类型时使用访问token来调用资源 一旦客户端有了访问token它就会使用该token来调用资源服务器上的api这与授权码授予类型完全一样。客户端将访问token添加到授权请求头的请求中。 回到我在第12.3.1节所做的类比想象一下卖书的女士没有给我打电话确认我想让我的朋友去拿书。我反而会把我的身份证给我的朋友以证明我委托我的朋友去取书。看到区别了吗在这个流程中我需要与客户分享我的ID凭证。出于这个原因我们说这种授予类型只适用于资源所有者 信任 客户端的情况。 密码授予类型不如授权码授予类型安全主要是因为它假定与客户端应用程序共享用户凭证。虽然它确实比授权码授予类型更直接但在现实世界的场景中尽量避免这种授予类型。即使授权服务器和客户端都是由同一个组织建立的你也应该首先考虑使用授权码授予类型。 把密码授予类型作为你的第二个选择。 12.3.3 客户凭证授予类型的使用 在本节中我们将讨论客户凭证授予类型图12.8。这是OAuth 2所描述的授予类型中最简单的一种。我喜欢把客户凭证授予类型看作是密码授予类型和API密钥认证流程的结合。我们假设你有一个用OAuth 2实现认证的系统现在你需要允许外部服务器进行认证并调用你的服务器所暴露的特定资源。 图 12.8 客户端凭证授予类型。如果客户需要访问一个资源但不代表资源所有者我们就使用这个流程。这个资源可以是一个不属于用户的api。 客户凭证授予类型的步骤与密码授予类型类似。唯一的例外是访问token的请求不需要任何用户凭证。下面是实现这种授予类型的步骤 请求一个访问token使用访问token来调用资源 第1步用客户凭证授予类型请求访问token 为了获得一个访问token客户端向授权服务器发送一个请求并提供以下细节。 grant_type值为client_credentials。client_id和client_secret代表客户的证书。scope它代表了被授予的权力 作为响应客户端会收到一个访问token。客户端现在可以使用该访问token来调用资源服务器的端点。 第2步使用访问token来调用具有客户凭证授予类型的资源 一旦客户端有了访问token它就会使用该token来调用资源服务器上的api这与授权码授予类型和通字授予类型完全一样。客户端将访问token添加到授权请求头中。 12.3.4 使用刷新token来获得新的访问token 在本节中我们讨论刷新token图12.9。到目前为止你已经了解到OAuth 2流程的结果我们也称之为授予是一个访问token。但我们并没有对这个token说太多。最后OAuth 2并没有为token假设一个特定的实现。你现在要学习的是无论如何实现token都会过期。这不是强制性的–你可以创建具有无限寿命的token–但是一般来说你应该使这些token尽可能短命。我们在本节中讨论的刷新token代表了一种获得新访问token的替代方法即使用信用证。我向你展示了刷新token在OAuth 2中的工作原理你还会在第13章看到这些token在一个应用程序中的实现。 让我们假设在你的应用程序中你实现了永不过期的token。这意味着客户端可以反复使用同一个token来调用资源服务器上的资源。 如果token被盗怎么办最后别忘了token是作为一个简单的HTTP头附在每一个请求上的。如果token不会过期拿到token的人就可以用它来访问资源。一个不会过期的token是太强大了。它几乎变得和用户凭证一样强大。我们倾向于避免这种情况并使token的有效期短一些。这样在某些时候过期的token就不能再使用了。客户端必须获得另一个访问token。 为了获得一个新的访问token客户端可以重新运行流程这取决于所使用的授予类型。例如如果授予类型是认证码客户端会将用户重定向到授权服务器的登录api用户必须再次填写他们的用户名和密码。这对用户并不友好不是吗想象一下token有20分钟的寿命而你用在线应用工作了几个小时。在这段时间里应用程序会重定向你让你再次登录。 了避免重新认证的需要授权服务器可以发出一个刷新token它的价值和目的与访问token不同。应用程序使用刷新token获得一个新的访问token而不需要重新认证。 在密码授予类型中刷新token也比重新认证有优势。即使是密码授予类型如果我们不使用刷新token我们将不得不要求用户再次认证或存储他们的凭证。在使用密码授予类型时存储用户凭证是你可能犯的最大错误之一。而且我已经看到这种方法在实际应用中的使用!请不要这样做!如果你存储了用户名和密码假设你把这些保存为明文或可逆的东西因为你必须能够重复使用它们你就暴露了这些凭证。刷新token可以帮助你轻松、安全地解决这个问题。你可以存储一个刷新token并在需要时使用它来获得一个新的访问token而不是不安全地存储凭证也不需要每次都重定向用户。存储刷新token是比较安全的因为如果你发现它被暴露你可以撤销它。此外不要忘记人们往往对多个应用程序拥有相同的凭证。因此丢失凭证比丢失一个可以在特定应用中使用的token更糟糕。 最后让我们看看如何使用刷新token。你从哪里得到一个刷新token当使用像授权码或密码授予类型的流程时授权服务器会将刷新token与访问token一起返回。对于客户凭证授予没有刷新token因为这个流程不需要用户凭证。一旦客户端有了刷新token当访问token过期时客户端应该发出一个包含以下细节的请求: grant_type的值为refresh_token。refresh_token,其值为刷新token的值。client_id和client_secret。scope 其中定义了相同的授予权限或更少。如果需要授权更多的授予权限则需要重新认证。 作为对该请求的响应授权服务器发出一个新的访问token和一个新的刷新token。 12.4 OAuth 2的缺点 在本节中我们将讨论OAuth 2认证和授权可能存在的漏洞。了解在使用OAuth 2时可能出现的问题是很重要的这样你就可以避免这些情况。当然就像软件开发中的其他东西一样OAuth 2并不是无懈可击的。它有自己的弱点我们必须意识到这些弱点并且在构建我们的应用程序时必须考虑这些弱点。我在这里列举了一些最常见的。 在客户端使用跨站请求伪造CSRF–在用户登录的情况下如果应用程序没有应用任何CSRF保护机制CSRF是可能发生的。我们在第10章中对Spring Security实现的CSRF保护进行了讨论。窃取客户凭证–存储或转移未受保护的凭证会产生漏洞使攻击者能够窃取和使用这些凭证。重放token–正如你将在第13章和第14章中所学到的token是我们在OAuth 2认证和授权架构中用来访问资源的钥匙。你通过网络发送这些东西但有时它们可能会被截获。如果被截获它们就会被盗并可以被重新使用。想象一下你丢失了你家前门的钥匙。可能会发生什么其他人可以用它来打开门只要他们愿意。我们将在第14章学习更多关于token的知识以及如何避免token重放。token劫持-暗示有人干扰认证过程并窃取他们可以用来访问资源的token。这也是使用刷新token的一个潜在漏洞因为这些token也可以被拦截并用于获得新的访问token。 记住OAuth 2是一个框架。漏洞是在它上面错误地实现功能的结果。使用Spring Security已经可以帮助我们减轻应用程序中的大部分漏洞。 12.5 实现一个简单的单点登录应用程序 在本节中我们将实现本书中第一个使用OAuth 2框架与Spring Boot和Spring Security的应用。这个例子向你展示了如何用Spring Security应用OAuth 2的总体概况。顾名思义单点登录SSO应用是指你通过授权服务器进行认证然后应用使用刷新token让你保持登录状态。在我们的案例中它只代表OAuth 2架构中的client。 在这个应用中图12.10我们使用GitHub作为授权和资源服务器并着重于组件之间的通信与授权代码授予类型。在第13章和第14章中我们将在一个OAuth 2架构中同时实现授权服务器和资源服务器。 图 12.10 我们的应用程序在 OAuth 2 架构中扮演着客户端的角色。我们使用GitHub作为授权服务器但它也承担了资源服务器的角色这使我们能够检索到用户的详细信息。 12.5.1 管理授权服务器 在这一节中我们将配置授权服务器。在本章中我们不会实现我们自己的授权服务器而是使用一个现有的服务器。GitHub。在第13章你将学习如何实现自己的授权服务器。 那么我们应该如何使用像GitHub这样的第三方作为授权服务器呢 这意味着最终我们的应用程序不会管理其用户任何人都可以使用他们的GitHub账户登录我们的应用程序。就像其他授权服务器一样GitHub 需要知道它向哪个客户端应用程序发放token。 记得在第 12.3 节中我们讨论了 OAuth 2 的授予请求使用了一个客户端 ID 和一个客户端密码。客户端使用这些凭证在授权服务器上验证自己因此OAuth应用程序必须在GitHub授权服务器上注册。要做到这一点我们要使用下面的链接完成一个简短的表单提交图12.11。 https://github.com/settings/applications/new 图 12.11 要把你的应用程序作为 OAuth 2 客户端以 GitHub 作为授权服务器你必须先注册它。你可以通过填写表格在 GitHub 上添加一个新的 OAuth 应用程序来完成。 当你添加一个新的OAuth应用时你需要为该应用指定一个名称主页以及GitHub将对你的应用进行回调的链接。与此相关的OAuth 2授予类型是授权码授予类型。这种授予类型假定客户端将用户重定向到授权服务器在我们的例子中是GitHub进行登录然后授权服务器在定义的URL上回调客户端正如我们在12.3.1节所讨论的那样。这就是为什么你需要在这里确定回调URL的原因。因为我在自己的系统上运行这个例子所以我在两种情况下都使用localhost。而且因为我没有改变端口默认是8080你已经知道了这使得http://localhost:8080 我的主页URL。我在回调中使用相同的URL。 一旦你填完表格并选择注册申请GitHub 就会为你提供一个客户端 ID 和一个客户端密码图 12.12。 图 12.12 当你在 GitHub 注册一个 OAuth 应用时你会收到客户端的凭证。你可以在你的应用程序配置中使用这些证书。 15a2c5a0e3fe921a223d 43cfa8dea92894ed7c26ec1ced76ee9354e15e15注意 我删除了你在图片中看到的应用程序。因为这些证书提供了对机密信息的访问我不能让它们继续存在。由于这个原因你不能重复使用这些凭证你需要按照本节的要求生成你自己的凭证。另外在使用这些凭证编写应用程序时要小心尤其是在使用公共的 Git 仓库来存储这些凭证时。 这个配置是我们为授权服务器所需要做的一切。现在我们有了客户证书我们就可以开始开发我们的应用程序了。 12.5.2 开始实现 在本节中我们开始实现一个SSO应用。我们创建一个新的Spring Boot应用程序并在pom.xml文件中添加以下的依赖项。 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId /dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId /dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-oauth2-client/artifactId /dependency我们首先需要一个web页面。为了做到这一点我们创建了一个controller类和一个简单的HTML页面来代表我们的应用程序。下面列出了MainController类它定义了我们应用程序的单一api。 代码清单12.1 controller类 package com.hashnode.proj0001firstspringsecurity.controller;import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping;import java.util.logging.Logger;Controller public class MainController {private Logger logger Logger.getLogger(MainController.class.getName());GetMapping(/)public String main(OAuth2AuthenticationToken token) {logger.info(String.valueOf(token));return main.html;} }我还在我的Spring Boot项目的资源/静态文件夹中定义了main.html页面。它只包含标题文本以便我在访问该页面时可以观察到以下情况: Hello there! 现在是真正的工作!让我们来设置安全配置让我们的应用程序能够使用GitHub的登录方式。我们先写一个配置类就像我们习惯的那样。 我们扩展WebSecurityConfigurerAdapter并重写configureHttpSecurity http方法。现在有一个不同之处我们没有像在第四章中学到的那样使用httpBasic()或formLogin()而是调用一个不同的方法名为oauth2Login()。这段代码在下面的代码中呈现。 代码清单12.2 配置类 package com.hashnode.proj0001firstspringsecurity.config;import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;Configuration public class ProjectConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(HttpSecurity http) throws Exception {//设置认证方法http.oauth2Login();//指定一个用户需要经过认证才能提出请求http.authorizeRequests().anyRequest().authenticated();} } 在代码清单12.2中我们在HttpSecurity对象上调用一个新方法oauth2Login()。和httpBasic()或formLogin()一样oauth2Login()只是在过滤器链中添加了一个新的认证过滤器。 我们在第9章中讨论了过滤器在那里你了解到Spring Security有一些filter实现你也可以在过滤器链中添加自定义的。在这种情况下当你调用oauth2Login()方法时框架添加到过滤器链的过滤器是OAuth2LoginAuthenticationFilter图12.13。这个过滤器拦截请求并为OAuth 2认证应用所需的逻辑。 图12.13 通过调用HttpSecurity对象的oauth2Login()方法我们将OAuth2LoginAuthenticationFilter添加到过滤器链中。它拦截请求并应用OAuth 2认证逻辑。 12.5.3 实现客户注册 在本节中我们将讨论如何实现OAuth 2客户端和授权服务器之间的链接。如果你想让你的应用程序真正有所作为这一点至关重要。 如果你现在按原样启动它你将无法访问主页面。不能访问该页面的原因是你指定了对于任何请求用户都需要进行认证但你没有提供任何认证的方式。我们需要确定 GitHub 是我们的授权服务器。为此Spring Security定义了ClientRegistration接口。 ClientRegistration接口代表OAuth 2架构中的客户端。对于客户端你需要定义其所有需要的细节其中我们有 客户端ID和密码用于认证的授予类型重定向URI范围 你可能还记得第12.3节应用程序需要所有这些细节来进行认证过程。Spring Security还提供了一种简单的方法来创建一个构建器的实例类似于你从第2章开始用来构建UserDetails实例的方法。代码清单12.3显示了如何用Spring Security提供的构建器来构建这样一个实例来代表我们的客户端实现。 代码清单12.3 创建一个ClientRegistration实例 private ClientRegistration clientRegistration() {ClientRegistration cr ClientRegistration.withRegistrationId(github).clientId(a7553955a0c534ec5e6b).clientSecret(1795b30b425ebb79e424afa51913f1c724da0dbb).scope(new String[]{read:user}).authorizationUri(https://github.com/login/oauth/authorize).tokenUri(https://github.com/login/oauth/access_token).userInfoUri(https://api.github.com/user).userNameAttributeName(id).clientName(GitHub).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).redirectUriTemplate({baseUrl}/{action}/oauth2/code/{registrationId}).build();return cr; }所有这些细节是怎么来的我知道清单12.3第一眼看上去很吓人但它只不过是设置了客户的ID和密码。另外在清单12.3中我定义了作用域授予的权限、一个客户名称和一个我选择的注册ID。除了这些细节我还必须提供授权服务器的URL Authorization URI 客户端重定向用户进行认证的URI。Token URI 客户端为获得访问token和刷新token而调用的URI如12.3节所述User info URI 客户端在获得访问token后可以调用的URI以获得关于用户的更多细节。 我是从哪里得到这些URI的好吧如果授权服务器不是你开发的就像我们的情况一样你需要从文档中获取它们。以GitHub为例你可以在这里找到它们 https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/ Spring Security甚至比这更聪明。该框架定义了一个名为CommonOAuth2Provider的类。这个类部分地定义了你可以用于认证的最常见的提供者的ClientRegistration实例其中包括 GoogleGitHubFacebookOkta 清单12.4 使用CommonOAuth2Provider类 private ClientRegistration clientRegistration() {return CommonOAuth2Provider.GITHUB.getBuilder(github).clientId(a7553955a0c534ec5e6b).clientSecret(1795b30b425ebb79e424afa51913f1c724da0dbb).build(); }正如你所看到的这要干净得多你不必手动寻找和设置授权服务器的URL。当然这只适用于普通提供商。如果你的授权服务器不在常见的提供者之列那么你没有其他选择只能完全按照清单12.3中的定义ClientRegistration。 注意 使用CommonOAuth2Provider类的值也意味着你依赖于你使用的提供者不会改变URL和其他相关值。虽然这不太可能但如果你想避免这种情况可以选择实现代码清单12.3中的ClientRegistration。这使得你可以在配置文件中配置URL和相关的提供者的值。 在本节的最后我们为配置类添加了一个私有方法该方法将返回ClientRegistration对象如下表所示。在第12.5.4节中你将学习如何为Spring Security注册这个客户端注册对象以便将其用于验证。 清单12.5 在配置类中建立ClientRegistration对象 package com.laurentiuspilca.ssia.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.core.AuthorizationGrantType;Configuration public class ProjectConfig extends WebSecurityConfigurerAdapter {private ClientRegistration clientRegistration() {return CommonOAuth2Provider.GITHUB.getBuilder(github).clientId(a7553955a0c534ec5e6b).clientSecret(1795b30b425ebb79e424afa51913f1c724da0dbb).build();}Overrideprotected void configure(HttpSecurity http) throws Exception {http.oauth2Login();http.authorizeRequests().anyRequest().authenticated();} }12.5.4 实现ClientRegistrationRepository 在这一节中你将学习如何为Spring Security注册ClientRegistration实例以用于认证。。为此Spring Security使用一个ClientRegistrationRepository类型的对象图12.14。 图12.14 ClientRegistrationRepository 检索ClientRegistration细节客户ID客户密码URL作用域等等。认证过滤器需要这些细节用于认证流程。 ClientRegistrationRepository接口类似于UserDetailsService接口你在第二章已经了解到。就像UserDetailsService对象通过用户名找到UserDetails一样Client-RegistrationRepository对象通过注册ID找到ClientRegistration。 你可以实现ClientRegistrationRepository接口来告诉框架在哪里可以找到ClientRegistration实例。Spring Security为我们提供了一个ClientRegistrationRepository的实现它将ClientRegistration的实例存储在内存中。正如你所猜测的这与InMemoryUserDetailsManager对UserDetails实例的工作原理类似。我们在第三章讨论了InMemoryUserDetailsManager。 为了结束我们的应用实现我使用InMemoryClientRegistrationRepository实现定义了一个ClientRegistrationRepository并将其注册为Spring上下文中的一个bean。 我把我们在12.5.3节中建立的ClientRegistration实例添加到InMemoryClientRegistrationRepository中把它作为参数提供给InMemoryClientRegistrationRepository构造函数。你可以在下一个代码清单中找到这段代码。 代码清单12.6 注册ClientRegistration对象 方式1 package com.hashnode.proj0001firstspringsecurity.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;Configuration public class ProjectConfig extends WebSecurityConfigurerAdapter {private ClientRegistration clientRegistration() {return CommonOAuth2Provider.GITHUB.getBuilder(github).clientId(a7553955a0c534ec5e6b).clientSecret(1795b30b425ebb79e424afa51913f1c724da0dbb).build();}public ClientRegistrationRepository clientRepository() {ClientRegistration c clientRegistration();return new InMemoryClientRegistrationRepository©;}Overrideprotected void configure(HttpSecurity http) throws Exception {//设置认证方法http.oauth2Login(c - {c.clientRegistrationRepository(clientRepository());});//指定一个用户需要经过认证才能提出请求http.authorizeRequests().anyRequest().authenticated();} } 方式2 package com.laurentiuspilca.ssia.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.core.AuthorizationGrantType;Configuration public class ProjectConfig extends WebSecurityConfigurerAdapter {private ClientRegistration clientRegistration() {return CommonOAuth2Provider.GITHUB.getBuilder(github).clientId(a7553955a0c534ec5e6b).clientSecret(1795b30b425ebb79e424afa51913f1c724da0dbb).build();}public ClientRegistrationRepository clientRepository() {var c clientRegistration();return new InMemoryClientRegistrationRepository©;}Overrideprotected void configure(HttpSecurity http) throws Exception {http.oauth2Login();http.authorizeRequests().anyRequest().authenticated();} }注意 一个配置选项和另一个一样好但请记住我们在第二章中讨论的内容。为了使你的代码易于理解避免混合配置方法。要么使用在上下文中用bean设置每一个东西的方法要么使用代码内联配置风格。 12.5.5 Spring Boot配置的魅力 在这一节中我将向你展示第三种方法直接从属性文件中构建ClientRegistration和ClientRegistrationRepository对象。这种方法在Spring Boot项目中并不罕见。我们也可以看到这种情况发生在其他对象上。 例如我们经常看到根据属性文件配置的数据源。下面的代码片断显示了如何在application.properties文件中为我们的例子设置client registration。 spring.security.oauth2.client.registration.github.client-ida7553955a0c534ec5e6b spring.security.oauth2.client.registration.github.client-secret1795b30b425ebb79e424afa51913f1c724da0dbb在这个片段中我只需要指定客户端ID和客户端密码。因为提供者的名字是githubSpring Boot知道从CommonOAuth2Provider类中获取有关URI的所有细节。现在我的配置类看起来就像下面代码清单。 Configuration public class ProjectConfigextends WebSecurityConfigurerAdapter {Overrideprotected void configure(HttpSecurity http) throws Exception {http.oauth2Login();http.authorizeRequests().anyRequest().authenticated();} }我们不需要指定关于ClientRegistration和ClientRegistrationRepository的任何细节因为它们是由Spring Boot根据属性文件自动创建的。如果我们使用Spring Security已知的常见提供商以外的提供商我们还需要使用以spring.security.oauth2.client.provider开头的属性组来指定授权服务器的细节。下面的代码片段为你提供了一个例子。 spring.security.oauth2.client.provider.myprovider.authorization-urisome uri spring.security.oauth2.client.provider.myprovider.token-urisome uri在我需要在内存中拥有一个或多个authentication providers的情况下就像我们在当前的例子中所做的那样我更喜欢按照我在本节中介绍的方式进行配置。它更干净更容易管理。 但是如果我们需要一些不同的东西比如将客户的注册信息存储在数据库中或者从网络服务中获取那么我们就需要创建一个自定义的ClientRegistration- Repository实现。在这种情况下我们需要按照你在12.5.5节中学到的方法来设置它。 12.5.6 获取已验证用户的详细信息 在本节中我们将讨论获取和使用认证用户的详细信息。你已经知道在Spring Security架构中是由SecurityContext来存储认证用户的详细信息。一旦认证过程结束负责的过滤器会将认证对象存储在SecurityContext中。应用程序可以从那里获取用户的详细信息并在需要时使用它们。同样的情况也发生在OAuth 2认证中。 在这种情况下框架所使用的认证对象的实现被命名为OAuth2AuthenticationToken。你可以直接从SecurityContext中获取它或者让Spring Boot在api的一个参数中为你注入它正如你在第6章中学到的那样。下面的列表显示了我如何改变控制器来接收并在控制台中打印用户的详细信息。 代码清单12.9 使用已登录用户的详细信息 package com.hashnode.proj0001firstspringsecurity.controller;import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping;import java.util.logging.Logger;Controller public class MainController {private Logger logger Logger.getLogger(MainController.class.getName());GetMapping(/)public String main(OAuth2AuthenticationToken token) {//Spring Boot自动在方法的参数中注入代表用户的Authentication对象。logger.info(String.valueOf(token));return main.html;} }12.5.7 测试应用程序 在本节中我们将测试本章中的应用程序。在检查功能的同时我们遵循OAuth 2授权代码授予类型的步骤图12.15以确保你正确理解它并观察Spring Security是如何通过我们的配置来应用它的。 图12.15 该应用将GitHub作为授权服务器同时也作为资源服务器。 当用户想要登录时客户端会将用户重定向到GitHub的登录页面。当用户成功登录后GitHub 会向我们的应用程序回拨一个授权码。我们的应用程序使用该授权码来请求一个访问token。然后应用程序可以通过提供访问token从资源服务器GitHub访问用户详情。资源服务器的响应提供了用户的详细信息和主页面的URL。 我首先确定我没有登录到GitHub。我还确保我打开了一个浏览器来检查请求导航的历史。这个历史记录可以让我了解OAuth 2流程中发生的步骤也就是我们在12.3.1节中讨论的步骤。如果我通过了认证那么应用程序就会直接记录我。然后我启动应用程序在浏览器中访问我们应用程序的主页面 应用程序将我重定向到以下代码片段中的URL并在图12.16中预发。这个URL被配置在GitHub的CommonOauth2Provider类中作为授权URL。 https://github.com/login?client_id15a2c5a0e3fe921a223dreturn_to%2Flogin%2Foauth%2Fauthorize%3Fclient_id%3D15a2c5a0e3fe921a223d%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A9090%252Flogin%252Foauth2%252Fcode%252Fgithub%26response_type%3Dcode%26scope%3Dread%253Auser%26state%3DSS5dh9rn0evuxXqBc_-wvgdlecYB8VpcTB-EgcGgtqg%253D图 12.16 进入主页面后浏览器将我们重定向到 GitHub 登录。在 Chrome 的控制台工具中我们可以看到对 localhost 的调用然后是对 GitHub 授权api的调用。 我们的应用程序将所需的查询参数附加到URL上正如我们在第12.3.1节所讨论的那样。这些参数是 response_type其值为codeclient_idscope值read:user也定义在CommonOauth2Provider类中。state值为CSRF令牌 我们使用我们的GitHub凭证用GitHub登录我们的应用程序。正如你在图12.17中看到的那样我们通过了认证并被重定向回来。 图 12.17 填写完凭证后GitHub 将我们重定向到我们的应用程序。现在我们可以看到主页面应用程序可以利用访问令牌从 GitHub 访问用户的详细信息。 下面的代码片断显示了GitHub给我们响应的URL。你可以看到GitHub提供了授权码我们的应用程序使用该授权码来请求获取token。 http://localhost:9090/login/oauth2/code/github?code4ad786953479921c594dstateyH8bDv81m9QsIRCT3urcU67forv-GSGuR3QulqAKH7Y可以看到返回的token如下 我们不会从浏览器中看到对token对象获取 api的调用因为这直接发生在我们的应用程序中。但我们可以相信应用程序设法获得了一个token对象因为我们可以看到控制台中打印的用户详细信息。这意味着应用程序获取了token对象。接下来的代码片段向你展示了这个输出的一部分。 Name: [43921235], Granted Authorities: [[ROLE_USER, SCOPE_read:user]], User Attributes: [{loginlspil, id43921235, node_idMDQ6VXNlcjQzOTIxMjM1, avatar_urlhttps://avatars3.githubusercontent.com/u/43921235?v4, gravatar_id, urlhttps://api.github.com/users/lspil, html_urlhttps:// github.com/lspil, followers_urlhttps://api.github.com/users/lspil/ followers, following_urlhttps://api.github.com/users/lspil/following{/ other_user}, …总结 OAuth 2框架描述了允许一个实体代表其他人访问资源的方法。我们在应用程序中使用它来实现认证和授权逻辑。一个应用程序可以用来获取访问令牌的不同流程被称为授予。根据系统结构的不同你需要选择一个合适的授予类型。 认证码授予类型的工作方式是允许用户直接在授权服务器上进行认证这使得客户端可以获得一个访问令牌。当用户不信任客户端并且不想与之分享他们的凭证时我们选择这种授予类型。密码授予类型意味着用户与客户端共享其凭证。只有在你能信任客户端的情况下你才应该应用这个。客户端凭证授予类型意味着客户端只通过验证其凭证来获得令牌。当客户端需要调用资源服务器的一个不是用户资源的api时我们选择这种授予类型。 Spring Security实现了OAuth 2框架允许你用几行代码在你的应用程序中实现它。在Spring Security中你可以使用ClientRegistration的一个实例来表示客户在授权服务器上的注册。Spring Security OAuth 2实现中负责寻找特定客户端注册的组件被称为ClientRegistrationRepository。在用Spring Security实现OAuth 2客户端时你需要定义一个至少有一个客户端注册的ClientRegistrationRepository对象。
- 上一篇: 南昌电子商务网站建设手机网站横竖屏
- 下一篇: 南昌个人网站建设新乡商城网站建设哪家专业
相关文章
-
南昌电子商务网站建设手机网站横竖屏
南昌电子商务网站建设手机网站横竖屏
- 技术栈
- 2026年03月21日
-
南昌成都网站建设方案太原网站建设方案推广
南昌成都网站建设方案太原网站建设方案推广
- 技术栈
- 2026年03月21日
-
南部县建设局网站北京建设集团网站首页
南部县建设局网站北京建设集团网站首页
- 技术栈
- 2026年03月21日
-
南昌个人网站建设新乡商城网站建设哪家专业
南昌个人网站建设新乡商城网站建设哪家专业
- 技术栈
- 2026年03月21日
-
南昌公司网站建设西安百度公司怎么样
南昌公司网站建设西安百度公司怎么样
- 技术栈
- 2026年03月21日
-
南昌模板建站代理辽宁建设工程信息网中标公示几天
南昌模板建站代理辽宁建设工程信息网中标公示几天
- 技术栈
- 2026年03月21日
