Azure AD统一认证及用户数据同步开发指导

本文主要目的为:指导开发者进行自有服务与Azure AD统一认证的集成,以及阐述云端用户数据同步的实现方案。本文除了会介绍必要的概念、原理、流程外,还会包含Azure门户设置说明,以及使用Fiddler进行全流程的实操验证,同时还会结合实际的业务需求提出具体的方案建议及关键实现方法的说明,以此达到开发指导的效果。

1. 基础概念

1.1 Azure AD

Azure Active Directory,简称 Azure AD,是微软提供的云身份验证服务,用于管理用户身份和访问权限。它是基于标准的开放式身份协议构建的,可以与多种应用程序和服务集成,包括云应用程序、本地应用程序和移动应用程序。
在这里插入图片描述

1.1.1 Azure AD 主要功能

  • 身份验证和授权管理: Azure AD 提供了多种身份验证方式,包括用户名和密码、多重身份验证、SSO(单一登录)等,以确保用户身份的安全性。它还可以管理用户对资源的访问权限,并控制用户能够执行的操作。
  • 集成式身份管理: Azure AD 可以与其他标识提供者集成,包括本地 Active Directory、LDAP、SAML 和 OAuth,使用户可以使用他们已有的帐户登录到 Azure AD 中的应用程序和服务。
  • 应用程序集成: Azure AD 支持与各种应用程序和服务的集成,包括 Microsoft 365、Azure、SaaS 应用程序、自定义应用程序等。它提供了一种安全的方式来管理和控制用户对这些应用程序的访问权限。
  • 安全和合规性: Azure AD 提供了安全性和合规性功能,包括多重身份验证、条件访问、安全策略、审计日志等,以帮助组织保护其数据和资源。

总之,Azure AD 是一个灵活且功能强大的身份验证和访问管理平台,可以帮助组织实现身份验证、授权和安全管理的统一化。从本质上来说,Azure AD 是基于 OAuth 2.0 和 OpenID Connect 规范的。OAuth 2.0 是一种用于授权的开放标准,用于安全地委托访问令牌,而 OpenID Connect 则是在 OAuth 2.0 的基础上添加了身份验证的协议。Azure AD 结合了这两种标准,以提供身份验证和授权功能,支持多种身份验证方法,并提供与其他服务和应用程序的集成。

需要补充说明的是Microsoft 将 Azure Active Directory (Azure AD) 更名为 Microsoft Entra ID,以介绍产品的多云多平台功能、缓解与 Windows Server Active Directory 的混淆,并统一 Microsoft Entra 产品系列。详见官方文档:https://learn.microsoft.com/zh-cn/entra/

1.1.2 Azure AD 与 ADFS区别

Azure AD 和 Active Directory Federation Services(ADFS)都是由微软提供的身份认证解决方案,但它们在一些方面有所不同:

  • 部署位置
    Azure 统一认证是基于云的身份认证解决方案,完全托管在 Microsoft Azure 云平台上。
    ADFS 是一个本地部署的身份认证解决方案,通常部署在组织的本地网络中。
  • 适用范围
    Azure 统一认证是为云环境和混合云环境设计的,可以为云服务、SaaS 应用程序以及本地应用程序提供身份验证和访问控制。
    ADFS 主要用于提供单点登录 (SSO) 和基于标准的身份验证服务,通常用于本地应用程序和企业资源的身份认证。
  • 标准支持
    Azure 统一认证支持 OAuth 2.0 和 OpenID Connect 等开放标准,使其能够与多种应用程序和服务集成。
    ADFS 支持标准的身份验证协议,如SAML(Security Assertion Markup Language)和 WS-Federation(Web Services Federation)。
  • 管理和维护
    Azure 统一认证是一种完全托管的服务,由 Microsoft 管理和维护,无需组织自行管理基础设施。
    ADFS 需要组织自行部署、配置和维护,需要更多的 IT 管理工作。
  • 灵活性和扩展性
    Azure 统一认证提供了更高的灵活性和扩展性,可以通过添加和配置不同的功能组件来满足不同的身份验证和授权需求。
    ADFS 在本地部署方面更加灵活,可以根据组织的特定需求进行自定义和配置。

综上所述,Azure 统一认证适用于云环境和混合云环境,提供了更多的托管功能和集成选项,而 ADFS 则更适用于需要本地部署、自定义和控制的场景。关于ADFS及AD的部署配置可以参考:https://blog.csdn.net/camelials/article/details/134857159

1.2 微软Graph

Microsoft Graph 是微软提供的统一 API 平台,用于访问 Microsoft 365 中的各种数据和服务。它提供了一种统一的方式来访问多种 Microsoft 产品和服务的数据,包括 Office 365、Azure Active Directory、Exchange Online、SharePoint 等。
在这里插入图片描述

理解 Microsoft Graph 可以从以下几个方面来看:

  • 统一访问数据和服务:Microsoft Graph 提供了一个统一的终结点,使开发者可以通过一组 API 来访问各种 Microsoft 产品和服务中的数据和功能,而不需要与每个服务单独交互。
  • 多种数据类型支持:Microsoft Graph 支持访问多种类型的数据,包括用户信息、邮件、日历、文件、组织结构等。这些数据可以用于构建各种类型的应用程序和解决方案。
  • RESTful API:Microsoft Graph 的 API 是基于 RESTful 架构的,使用标准的 HTTP 方法和 URL 结构进行访问。这使得它易于使用和集成到现有的应用程序和开发工具中。
  • 权限控制和安全性:Microsoft Graph 提供了严格的权限控制机制,开发者需要通过 OAuth 2.0 认证来获取访问权限,并在访问数据时遵循权限范围的规定,确保数据的安全性和隐私保护。
  • 实现业务逻辑:通过 Microsoft Graph,开发者可以实现各种业务逻辑,例如获取用户信息、发送邮件、管理文件、创建团队等。它可以帮助开发者构建智能化、协作化的应用程序和解决方案。

总的来说,Microsoft Graph 提供了一种统一、灵活的方式来访问 Microsoft 365 中的数据和服务,为开发者提供了丰富的功能和资源,帮助他们构建创新的应用程序和解决方案。Microsoft Graph 中的主要服务和功能可以参考官方文档:https://learn.microsoft.com/zh-cn/graph/overview-major-services

2. 统一认证流程及实操验证

微软Graph分为中国版(由上海世纪互联运营)和国际版,两者大致一样,但是也存在一些差异,例如:Portal门户设置UI不同,终结点不同,一些接口的参数和约定不同等,这里以全球版的v1.0进行介绍。

2.1 前置条件

2.1.1 应用程序注册/设置

1、基本设置
在这里插入图片描述
应用程序主要基本设置如上图,其中租户ID(tenant_id)、应用程序ID(client_id)、客户端凭据(client_secret)、重定向URL(redirect_uri)等在后续的授权及微软Graph API调用的时候需要使用。例如:

### 登录授权终结点
https://login.microsoftonline.com/#{tenant}/oauth2/v2.0/authorize?client_id=#{client_id}&response_type=code&redirect_uri=#{redirect_uri}### 授权码获取Token
POST https://login.microsoftonline.com/#{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencodedclient_id=#{client_id}
&scope=User.Read
&redirect_uri=#{redirect_uri}
&grant_type=authorization_code
&client_secret=#{client_secret}
&code=#{auth_code}

2、API权限设置
在这里插入图片描述
1、可以把Microsoft Graph理解为访问Microsoft 365 和 Azure 云端资源的网关,若要访问这些资源则需要用户或管理员向其授予所需的权限。如果权限设置不当,则在实际的访问中就会遇到各种权限相关错误,例如:Authorization_RequestDenied(Insufficient privileges to complete the operation.),因此权限控制是实操过程中经常被提及和困惑的问题。详细可以参考微软官方文档:https://learn.microsoft.com/zh-cn/graph/permissions-reference

2、从上图中的权限类型可以看到有2种:委托和应用程序,委托可以理解为用户认证成功之后的委托授权访问,为了好理解,我把这称之为:AppUser权限(需要用户授权);应用程序则不需要用户授权,而是由管理员授权,为了好理解和区分,可以称之为:AppOnly权限。举个例子:

  • AppUser权限:用户登录认证成功之后返回一个授权码,用这个授权码获得的Token就可以访问该登录用户自己的信息和资源,例如他自己的Profile信息,但是无权访问别人的相关资源。这就是AppUser权限的委派授权。
  • AppOnly权限:自有服务(AppServer)直接请求Azure令牌终结点获取Token,然后用该Token请求微软Graph获取该租户下所有的用户数据。

在这里把开通Directory.Read.All和User.Read.All的AppOnly权限的原因是对于用户数据同步的方案,我们将会选择Delta Query方案。对于用户数据同步的方案微软其实提供了2种,后面会详细讲。

2.2 AppUser方式授权

在这里插入图片描述
在了解了AppUser方式授权和AppOnly授权的差异之后,对于上面的流程图就能很容易的理解了,下面对于一些关键点的开发指导做进一步的描述:

1、AppServer需要向客户端提供一个后去登录认证地址的接口。认证终结点地址可以在Azure应用程序中进行查看,如下图:
在这里插入图片描述
同时,认证终结点还需要携带一些参数,示例如下(相关含义,前面的《应用程序注册/设置》章节已经进行过描述):

https://login.microsoftonline.com/#{tenant}/oauth2/v2.0/authorize?client_id=#{client_id}&response_type=code&redirect_uri=#{redirect_uri}

2、客户端拿到登录认证地址即可进行登录,如下图。一般来说,客户端会使用系统浏览器访问Azure授权终结点,那么会遇到一个问题:此时已不在客户端App的进程之内,那么后续该如何重新回到App,或者如何通知App登录认证完成呢?问题先放着,后面会讲。
在这里插入图片描述

3、登录完成之后Azure授权终结点会向事先配置好的回调终结发送一个POST请求,该请求会包含一个授权码,表示认证通过可以授权。下面演示通过使用Fiddler抓包去查看授权码:
在这里插入图片描述
通过上面的方法,我们可以获得授权码。由于这里只是测试,因此我把回调地址配置成了一个不存在的:https://www.baidu.com/login,因此404,但是这并不影响我们通过用Fiddler抓包去查看授权码。

4、拿到授权码后就可以请求Azure令牌终结点去获取Token了,请求示例如下:

### token[authorization_code]
POST https://login.microsoftonline.com/#{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencodedclient_id=#{client_id}
&scope=User.Read
&redirect_uri=#{redirect_uri}
&grant_type=authorization_code
&client_secret=#{client_secret}
&code=#{auth_code}

应答示例如下:

HTTP/1.1 200 OK
Cache-Control: no-store, no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
x-ms-request-id: 803bf994-b432-4ee8-8380-b1e370535b00
x-ms-ests-server: 2.1.17846.6 - SEASLR1 ProdSlices
x-ms-srs: 1.P
X-XSS-Protection: 0
Set-Cookie: fpc=Al_NmhqaTrVPmpaVIt5Vo10V53uDAQAAAD0mtN0OAAAA; expires=Sun, 19-May-2024 08:45:50 GMT; path=/; secure; HttpOnly; SameSite=None
Set-Cookie: x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly
Date: Fri, 19 Apr 2024 08:45:50 GMT
Content-Length: 2340{"token_type": "Bearer","scope": "User.Read profile openid email","expires_in": 5229,"ext_expires_in": 5229,"access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6Ik..."
}

需要补充说明的是access_token其实就是一个json的jwt序列化字符串,https://jwt.ms/ 可以进行在线解码:
在这里插入图片描述
其实使用com.auth0.jwt.JWT也可以进行解码,但是并不推荐通过这样的方式去获取用户信息,因为目前尚不明确微软对于算法是如何约定的,通过穷尽尝试几种常用的算法成功解码,假如有天微软把算法变了,程序不就出Bug了吗?这里,只是向告诉大家access_token的本质其实就是一个类似ADFS的授权Claims Json信息,常见的 JWT 签名算法有:

  • HMAC256:使用 HMAC(Hash-based Message Authentication Code)和 SHA-256 算法生成签名。
  • RS256:使用 RSA(Rivest-Shamir-Adleman)加密算法和 SHA-256 哈希算法生成签名,需要使用 RSA 公钥/私钥对进行签名和验证。
  • ES256:使用 ECDSA(Elliptic Curve Digital Signature Algorithm)和 SHA-256 哈希算法生成签名,需要使用 ECDSA 公钥/私钥对进行签名和验证。
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;public class JWTDecoder {public static void main(String[] args) {String jwtToken = "your_jwt_token_here";String secretKey = "your_secret_key_here";try {Algorithm algorithm = Algorithm.HMAC256(secretKey);JWTVerifier verifier = JWT.require(algorithm).build();DecodedJWT decodedJWT = verifier.verify(jwtToken);// 获取 JWT 中的用户 UPNString upn = decodedJWT.getSubject();System.out.println("User UPN: " + upn);} catch (Exception e) {// 处理异常e.printStackTrace();}}
}

5、拿到AccessToken之后就可以请求微软Graph根据业务处理的需要去后去资源了,一般业务场景下都需要首先获得当前登录用户的Profile信息,请求示例如下:

### me
GET https://graph.microsoft.com/v1.0/me
Authorization: Bearer #{access_token}

微软Graph参考资源:

  • https://learn.microsoft.com/zh-cn/graph/api/overview?view=graph-rest-1.0
  • https://developer.microsoft.com/en-us/graph/graph-explorer

6、前面抛出了一个问题:客户端会使用系统浏览器访问Azure授权终结点那么后续该如何重新回到App,或者如何通知App登录认证完成了呢?要解决这个问题其实方案有很多,例如:
1)集成微软SDK;
2)通过服务端输出合适的JS将控制权交给重新交给客户端;
3)重定向到一个中间站点并通过url参数方式传值;
4)将处理结果放在header中;
5)服务端下发通知给客户端(双工App应用,服务端可以向客户端下发通知);
这里我推荐使用方案2,示例JS脚本如下:

<html><header></header><body></body><script language='javascript'>if (window.ad) {window.ad.end(0, 'ok');}</script>
</html>

这段 JavaScript 脚本首先检查当前页面是否存在名为 “ad” 的全局对象或变量。如果存在,它调用 “ad” 对象的 “end” 方法,并传入两个参数:0 和 ‘ok’,其中,0 代表状态码,‘ok’ 为描述(code + msg 表达认证回调的服务端处理结果)。

通过服务端输出合适的JS将控制权交给重新交给客户端,这其实是目前的一种经典做法,因为JS是运行在客户端的Local上。上述JS脚本代码通常在网页中嵌入的广告脚本中使用,当广告成功加载并展示后,广告服务商通常会调用类似的方法来通知页面,以便页面做出相应的处理。

2.3 AppOnly授权

在这里插入图片描述
在了解了AppUser授权的相关内容后,AppOnly的授权就会十分简单了:AppServer直接请求Azure令牌终结点获取Token。当然,这里需要事先在Azure门户中对API权限进行应用程序授权(前面已经介绍过),请求示例如下:

### token[client_credentials]
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencodedclient_id=#{client_id}
&redirect_uri=#{redirect_uri}
&grant_type=client_credentials
&client_secret=#{client_secret}
&scope=https://graph.microsoft.com/.default

应答示例如下(这里贴个实测的图来证明确实可行)。
在这里插入图片描述
最后,大家可以用这种方式下获得的Token去调用微软Graph(当然,要访问的资源需要事先由Azure管理员授权),后面会涉及获取租户下所有的用户数据,用的token就是这种。大家可以比较下与AppUser授权方式获取Token的差异,那么就会发现grant_type和scope的不同。关于scope=https://graph.microsoft.com/.default的说明,大家可以去查阅微软官网。

3. 用户数据同步方案

Azure提供2种变更数据获取方案:订阅(webhook)方案、Delta Query方案,综合利弊建议大家选择Delta Query方案

方案
订阅方案1、对于Azure第三方应用程序服务端实现简单,只需要接收数据变更通知并予以处理即可;1、webhook方式接收方无法自主控制频度和重试(对于订阅方式,Azure 平台会尽力发送数据通知,但并不保证数据的实时性或完整性,在设计应用程序时应考虑到这一点,并实施适当的容错机制)
2、Azure订阅会有费用成本(计费策略因子为:订阅的数量、通知的频率、数据传输量等),而且可以设置费用告警阈值。这些可能产生解释/沟通成本,同时可能会被客户挑战:为什么不使用无费用的Delta Query方案。
3、webhook订阅数据通知只限于增量变化数据的获取,首次全量数据同步仍然需要从微软Graph接口中获取。
Delta Query方案1、可以自主控制数据获取的频度和失败重试。
2、不涉及成本问题的客户挑战。
3、首次全量数据获取与增量数据获取的方案和处理统一(Delta Query接口有类似版本号的可选参数,不携带该参数则为首次全量获取)
1、Azure第三方应用程序服务端实现相对复杂:需要自行考虑频度,重试等问题。同时还需要自行维护数据版本信息以实现增量数据获取。
2、需要一次性对应用程序API权限进行授权(AppOnly授权方式)。

1、Delta Query方案其实是一个轮询方案,那么就会涉及定时任务,当前可选的框架很多:Spring Framework 的任务执行器(TaskExecutor)、Quartz 、XX-Job等。至于说Delta Query的频度,这个大家根据业务需要自行指定(一天一次也好,多少小时分钟一次也好)。

2、Delta Query方案大家需要理解如何实现数据的增量获取,我给大家演示下首次全量数据获取和非首次增量数据获取大家就明白了。

  • 首次全量数据获取
    在这里插入图片描述
  • 非首次增量数据获取
    在这里插入图片描述
    通过对比上面2个请求和应答就可以很容易的发现:

每次Delta Query请求应答都会返回一个deltatoken,其实可以把这个deltatoken理解为资源的版本。那么就很容易的能够理解:首次全量数据获取就不携带deltatoken,要获取某个版本之后的增量数据就携带deltatoken去请求即可。

最后需要说明的是:对于订阅和Delta Query其实是Azure对于很多资源的通用方案,因此不只是users可以使用,其他的资源也可以使用。前面说了,可以把微软Graph理解为访问云端资源的网关,用户是资源,邮件是资源,outlook等都是资源。
在这里插入图片描述

4. 总结

1、要实现Azure AD统一认证与自己业务的系统的集成,一般来说自身业务系统需要实现2个接口即可:1)获取登录认证Url接口(getAuthUrl);2)认证回调处理接口(authCallback);
2、自身业务系统需要维护自身业务系统UID与Azure AD的用户唯一标识(一般为用户的UPN,这也是AD规范中所推荐使用的),可以把这个过程称之为绑定(大家可以参考下IDasS厂商的一些共同做法),对于已经绑定的用户数据就可以支持定时与云端的数据同步。
3、要实现Azure AD用户数据与自身业务系统用户数据的同步,虽然微软提供了2种方案(订阅方案和Delta Query方案),但是结合一般业务场景下的利弊分析后,建议选择Delta Query方案,但是这也不是绝对的,如何选择还得具体问题具体分析。

上述内容调研和折腾了几天的时间,整个过程也都进行了实操验证,而且我也尽量把一些概念、原理也都给予一定的介绍,最后个人认为做调研以下两点非常重要:
1)知其然,知其所以然。
2)一定要上手实操验证。

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

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

相关文章

HarmonyOS ArkUI实战开发-页面跳转(Router、Ability)

页面跳转可以分为页面内跳转和页面间跳转&#xff0c;页面内跳转是指所跳转的页面在同一个 Ability 内部&#xff0c;它们之间的跳转可以使用 Router 或者 Navigator 的方式&#xff1b;页面间跳转是指所跳转的页面属与不同的 Ability &#xff0c;这种跳转需要借助 featureAbi…

Java练习题

打印9*9乘法口诀表 解析&#xff1a;利用for循环解决 代码如图所示&#xff1a; public class Cc {public static void main(String[] args) {for (int i 1; i < 10; i){ //从1遍历到9 for(int j 1; j < i; j){ System.out.print(j "*" i "&…

Docker pull镜像名称 把本地镜像推送到远程详解

Docker pull镜像名称 把本地镜像推送到远程详解&#xff1a; Docker 镜像 仓库 容器介绍 以及镜像仓库详解 下载一个alpine的镜像演示&#xff0c;alpine是一个比较小的的linux镜像。 docker pull alpinedocker tag d4ff818577bc docker.io/itying/alpine:v1.0.1docker tag d4…

【CSS】使用 scroll snap 实现页面的垂直大屏滚动

CSS 属性 scroll-snap-type 设置了在有滚动容器的情形下吸附至吸附点的严格程度。 scroll-snap-type 使用 scroll snap 也可以用于垂直滚动&#xff0c;全屏展示就是一个很好的例子: <main><section class"section section-1"></section><sect…

Android驱动开发之如何编译和更换内核

编译内核可以使用图形化的界面配置,也可以直接使用脚本。在X86_64模拟器环境下,不用交叉编译,而交叉编译工具很容易出现兼容问题,一般也只能使用芯片厂商提供的工具,而不是GNU提供的工具。 android内核开发流程以及架构变化了很多,详情请看 内核官网 内核版本选择 由…

keil把c语言函数转成汇编

汇编可以让开发人员从根源上理解程序的运行逻辑&#xff0c;本文介绍如何在keil环境下如何把一个c文件中的某一个函数&#xff0c;转换为汇编函数&#xff0c;并编译运行。 右击某个c文件&#xff0c;选择Option for File。。。 图1 然后把下图中的Generate Assembler SRC Fi…

DDP、pytorch的分布式 torch.distributed.launch 训练说明

0、DDP的运行原理 执行步骤&#xff1a; 将data分为多个不同的batch&#xff0c;每个gpu得到batch都是不一样的然后将每个batch放在每个gpu上独立的执行最后得到的梯度求平均将平均梯度平分给每个gpu执行下一次迭代 这也就意味着你有多少个gpu&#xff0c;训练的速度也会提升…

数据结构与算法解题-20240422

这里写目录标题 一、2. 两数相加二、67. 二进制求和三、415. 字符串相加四、LCS 01. 下载插件五、71. 简化路径 一、2. 两数相加 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 …

uniapp——授权报错,选择合适的基础库

说明 我的小程序开发版本点击选择头像报错 更换基础库就好了

发布自己的Docker镜像到DockerHub

学会了Dockerfile生成Docker image 之后&#xff0c;如何上传自己的镜像到 DockerHub呢&#xff1f;下面我以自己制作的 bs-cqhttp 镜像为例&#xff0c;演示一下如何将自己的镜像发布到 Docker 仓库。 1 生成自己的 Docker 镜像 1.1 实例镜像用到的文件 图1 实例镜像制作用到…

vue 请求php接口 header 传自定义参数 提示cors 跨域问题

前端地址 http://192.168.0.125:4021 请求后端地址的时候报 from origin http://192.168.0.125:4021 has been blocked by CORS policy: Request header field userid is not allowed by Access-Control-Allow-Headers in preflight response. 大概意思是请求 header里有个…

3Darray 修改array值然后保存图片

from PIL import Image import numpy as np img_path ./000001.jpg # 读取图片 image Image.open(img_path) width, height image.size print("图片的宽度为{},高度为{}".format(width,height)) print("图片的mode为{}".format(image.mode)) print(&quo…

指针专题(4)【qsort函数的概念和使用】

1.前言 上节我们学习了指针的相关内容&#xff0c;本节我们在有指针的基础的条件下学习一下指针的运用&#xff0c;那么废话不多说&#xff0c;我们正式进入今天的学习 2.回调函数 我们既然已经学习了指针的相关基础&#xff0c;那么我们此时就可以用指针来实现回调函数 而回…

Linux学习之HTTP

引言&#xff1a;了解到了协议的定制&#xff0c;我们就知道在进行客户端与服务端通信时&#xff0c;对于报文的封装协议的定制是必不可少的&#xff0c;虽说协议是我们自己定制&#xff0c;但是有大佬们直接为我们写了一套完整的&#xff0c;可靠的协议&#xff0c;例如http协…

Python | Leetcode Python题解之第32题最长有效括号

题目&#xff1a; 题解&#xff1a; class Solution:def longestValidParentheses(self, s: str) -> int:stack[]maxL0nlen(s)tmp[0]*n #标记数组cur0for i in range(n):if s[i](:stack.append(i)else:if stack:jstack.pop()if s[j](:tmp[i],tmp[j]1,1 #匹配成…

cookie-editor 管理您的 cookie

Cookie-Editor - 适用于 Chrome、Firefox、Safari、Edge 和 Opera 的安全 cookie 编辑器https://cookie-editor.com/ Cookie-Editor 是一个专注于生产力的浏览器扩展&#xff0c;可帮助您以尽可能少的点击次数管理您的 cookie。 您最多只需点击三次&#xff0c;即可访问当前页…

龙迅LT8618SXB TTL /BT656/BT601/BT1120桥接到HDMI 1.4,低功耗HDMI1.4发射机

龙迅LT8618SXB描述&#xff1a; LT8618SX是Lontium基于ClearEdgeTM技术的低功耗版本HDMI发射机。它支持24位颜色深度HDMI1.4&#xff08;高清多媒体接口&#xff09;规范。它们完全向后兼容Lontium的第一代HDMI发射机LT8618EX。LT8618SX是一款高性能、低功耗的部件&#xff0c…

2W 隔离宽范围输入,单输出 DC/DC 电源模块——TP2L-2W 系列

TP2L-2W系列是一款高性能、超小型的电源模块&#xff0c;宽范围2&#xff1a;1输入&#xff0c;输出有稳压和连续短路保护功能&#xff0c;隔离电压为1.5KVDC、3KVDC&#xff0c;工作温度范围为–40℃到85℃。特别适合对输出电压的精度有严格要求的地方&#xff0c;外部遥控功能…

【STM32】嵌入式实验二 GPIO 实验:数码管

实验内容&#xff1a; 编写程序&#xff0c;在数码管上显示自己的学号。 数码管相关电路&#xff1a; PA7对应的应该是段码&#xff0c;上面的图写错了。 注意&#xff1a;选中数码管是低电平选中&#xff1b;并且用74HC595模块驱动输出的段码&#xff0c; 这个模块的学习可以…

JAVA基础之垃圾收集器

一 JVM垃圾收集 分代收集思想 当前虚拟机的垃圾收集一般采用分代收集算法&#xff0c;这种算法本身没有创新性&#xff0c;只是根据对象存活周期的不同将内存分为几块。一般将java堆内存分为新生代和老年代&#xff0c;这样我们就可以根据不同年龄到的特点选择不同的垃圾收集…