java ltpa_LTPA Cookie原理

1. 什么是LTPA?

Lightweight Third-Party Authentication (LTPA)是IBM Websphere和Domino产品中使用单点登录技术。当服务器配置好LTPA认证方式,用户通过浏览器成功登录后,服务器会自动发送一个session cookie给浏览器;此cookie中包含一个LTPA Token。

8nHIEUtDLn4EX64kBh6wFBfeNA5nY0NnnZRXTMPT3bYhml_IMi29rcA66_9zDwYCxsa7CZvvgd8a7Mcl4rS-aYq4zQ

2. WebSphere部分

本部分描述适用于已实施WebSphere系列产品应用和Domino平台应用,或WebSphere与Domino之间已完成单点登录。在这样的环境中与构异系统实现单点登录。

一个通过有效的LTPA Cookie能够在同一个认证域中所有服务器被自动认证。此Cookie中包含认证信息和时间戳。这些信息通过共享的3DES Key进行了bis 加密。使用公共密钥/私有密钥进行签名。

LTPA Cookie通过3DES密钥使用DESede/ECB/PKCS5P进行加密。此密钥也是采用DESede/ECB/PKCS5P进行加密,加密后再使用提供的密码再进行SHA Hash,生成24个字节的密钥,再进行Base64编码。

如果你要解析LTPA Token,先得使用Key的密码,生成3DES密钥;再使用3DES密钥解密Token Cookie。也可以使用公共/私有密钥来签名或验证LTPA Cookie。

2.1 WebSphere LTPA 生成原理

首先,这个 cookie 由以下部分组成,以%进行分隔:

用户信息,格式为u:user\:/,如:u:user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology

过期时间

签名信息,如:

u:user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology%1301558320666%Cy2CAeru5kEElGj0hrvYsKW2ZVsvvcu6Un573aeX55OO4G3EMYWc0e/ZbqDp1z7MS+dLzniuUH4sYWCMpnKdm7ZGabwmV+WcraBl+y+yzwcl722gHVMOnDZAW7U3jEay9Tk2yG4yXkMWU+617xndpVxke2jtS5wIyVVM3q7UDPw=

2.2 异构系统所需信息

从WebSphere系统中导出ltpa的key文件,使用文本文件打开,如:

com.ibm.websphere.CreationDate=Thu Mar 31 11\:08\:09 GMT+08\:00 2011 com.ibm.websphere.ltpa.version=1.0 com.ibm.websphere.ltpa.3DESKey=7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g\= com.ibm.websphere.CreationHost=wasserver com.ibm.websphere.ltpa.PrivateKey=N3bnOE1IbiXNsHXxxemC98iiCnmtw3JUuQvdFjEyh9r2gu+FlQRmG8xp5RBltqc6raI4EgYFhTr+t5/tmRQrFqfNKgvujeJZODeCspohi1V4C0qit7DOoqD9xOOn9Rzdb4PIuJM3ekwuBiZZYTYu7q0TANDygc7VbmwoD3xMPCk5svyvFJ/VshPyg5f7Q+VNM8dlIitU4gK9Qp8VZEqjGoXsYYzYYTQgnwAVtR2GfZtXKlf24EPXSkgUz9j8FwTvcylcKwjS22d6eVjciyAzInnxPqxE2iMRPEFDatHZFox3flsqBswmeDQrAGv8zIiffgP1DLKdjozUyAG+50v97xx7u1RtIrB4B01ik8DuLhw\= com.ibm.websphere.ltpa.Realm=VGOLiveRealm com.ibm.websphere.ltpa.PublicKey=AM04If2+ElGSyVRF0ZEesgvC59vGw8gSIfptjfoXj8iz4C7Ip/KVAu2PDkpQi3LUN/FgVF696tmsegBThks9rmMMHzOix/vGP2721dQZKbD7plOLdWtiY2AYZChsBVkOF26DfiWJ6euxD+a+KNcrfDnu2AXRC/tKncIUJV4LbeJdAQAB

所使用的DNS域,如:vgolive.com

过期时间(分钟),如:30

LTPA 3DESKey 密钥及密钥的保护密码

Base DN,如:O=VGOLive Technology或DC=vgolive,DC=com

注:

在3DESKey中的反斜杠只是为了在JAVA中可解释等于号,所以正确的3DESKey为 7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g=

用户名可以通过Base Dn验证字进行拼接,如异构系统中用户名为SquallZhong,相应在Domino中的DN就是CN=SquallZhong,O=DigiWin。此类情况仅限于LDAP中的CN与异构系统中的用户名一致。如果不一致,需要异构系统做用户对应

2.3 实现

Base64解码/编码所需Jar包: apache-commons-codec-1.3.jar以上

SHA-1的校验使用 java.security.MessageDigest类

2.3.1 解析

以下代码为解析从WebSphere或Domino发送过来的LTPAToken Cookie以Java为例:

01 …

02         // LTPA 3DES 密钥

03         String ltpa3DESKey = "7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g=" ;

04         // LTPA 密钥密码

05         String ltpaPassword = "Passw0rd" ;

06         try  {

07             // 获得加密key

08             byte [] secretKey = getSecretKey(ltpa3DESKey, ltpaPassword);

09             // 使用加密key解密ltpa Cookie

10             String ltpaPlaintext = new  String(decryptLtpaToken(tokenCipher,

11                     secretKey));

12             displayTokenData(ltpaPlaintext);

13         } catch  (Exception e) {

14             System.out.println( "Caught inner: "  + e);

15         }

16 …

17     //获得安全Key

18     private  static  byte [] getSecretKey(String ltpa3DESKey, String password)

19             throws  Exception {

20         // 使用SHA获得key密码的hash值

21         MessageDigest md = MessageDigest.getInstance( "SHA" );

22         md.update(password.getBytes());

23         byte [] hash3DES = new  byte [ 24 ];

24         System.arraycopy(md.digest(), 0 , hash3DES, 0 , 20 );

25         // 使用0替换后4个字节

26         Arrays.fill(hash3DES, 20 , 24 , ( byte ) 0 );

27         // BASE64解码 ltpa3DESKey

28         byte [] decode3DES = Base64.decodeBase64(ltpa3DESKey.getBytes());

29         // 使用key密码hash值解密已Base64解码的ltpa3DESKey

30         return  decrypt(decode3DES, hash3DES);

31     }

32     //解密LtpaToken

33     public  static  byte [] decryptLtpaToken(String encryptedLtpaToken, byte [] key)

34             throws  Exception {

35         // Base64解码LTPAToken

36         final  byte [] ltpaByteArray = Base64.decodeBase64(encryptedLtpaToken

37                 .getBytes());

38         // 使用key解密已Base64解码的LTPAToken

39         return  decrypt(ltpaByteArray, key);

40     }

41     // DESede/ECB/PKC5Padding解方法

42     public  static  byte [] decrypt( byte [] ciphertext, byte [] key)

43             throws  Exception {

44         final  Cipher cipher = Cipher.getInstance( "DESede/ECB/PKCS5Padding" );

45         final  KeySpec keySpec = new  DESedeKeySpec(key);

46         final  Key secretKey = SecretKeyFactory.getInstance( "TripleDES" )

47                 .generateSecret(keySpec);

48         cipher.init(Cipher.DECRYPT_MODE, secretKey);

49         return  cipher.doFinal(ciphertext);

50     }

51 …

解析出来的LTPAToken信息以%分隔

2.3.2 生成

Websphere LTPA生成时的签名信息是由用户DN和一些用户其他信息组成字符串,使用私有密钥进行签名,由于不清楚这些信息的组成,故无法产生正确的LTPA。

3. Domino部分

本部分的描述仅适用于单一的Domino平台应用与构异系统实现单点登录。

3.1 Domino LTPA Cookie 生成原理

在与 Domino 做 SSO 的时候,会使用 LTPA Token的认证方式,本文描述它的生成原理,通过它我们可以自己编码生成身份认证的 cookie,实现 SSO。

首先,这个 cookie 由以下部分组成:

LTPA token 版本(4字节)

创建时间(8字节)

过期时间(8字节)

用户名(可变长度)

Domino LTPA 密钥(20字节)

接下来分别说明各部分的具体内容:

LTPA token 版本目前 Domino 只有一种值:0x0001

创建时间为以十六进制方式表示的Unix time,例如:2009-04-09 13:52:42 (GMT +8) = 1239256362 = 49DD8D2A。

过期时间=创建时间 + SSO 配置文档的过期时间(LTPA_TokenExpiration域)

用户名为 Names 中用户文档的FullName域值;如:Squall Zhong/Digiwin

Domino LTPA 密钥通过 Base64编码后,保存在 SSO 配置文档的LTPA_DominoSecret域中

9aNb90qGxfI2xo079GBnZU70rVkt0VA0PptmOwkXa3lMWTC4NsYGlyN9SSs2KXnf-umJAFKFSBHwUYyHD89laYLElg

当然不能将密钥直接发送给浏览器,所以将上述部分合并起来(如上图),计算 SHA-1 校验和。

XLW2lNbFKVzFhL7hGYHbYxQFFy9VqrMdND0CRWaPYL8UyKzjOl7cPTiIjY_sJ5mP8QSNeAFZLT-8slUCXqXHKpvGSQ

然后用 SHA-1 校验和替换掉 Domino LTPA 密钥,最后再将内容通过 Base64 编码,形成最终的 cookie 发送给浏览器(如上图)。这样如果 cookie 中的任何内容被修改,校验和就不对了,达到了防篡改的效果。所以最终LTPA Cookie所得到的值为以下公式组成:

SHA-1=LTPA版本号+创建时间+过期时间+用户名+Domino LTPA 密钥

LTPA Cookie= Base64(LTPA版本号+创建时间+过期时间+用户名+SHA-1)

3.2 异构系统所需信息

Domino 所使用的DNS域,如:vgolive.com

过期时间(分钟),如:30

Domino LTPA 密钥

Domino验证字名称,如:/O=VGOLive Technology

注:用户名可以通过Domino验证字进行拼接,如异构系统中用户名为SquallZhong,相应在Domino中的DN就是CN=SquallZhong/O=VGOLive Technology。此类情况仅限于Domino中的CN与异构系统中的用户名一致。如果不一致,需要异构系统做用户对应

3.3 实现

Base64解码/编码所需Jar包: apache-commons-codec-1.3.jar以上

SHA-1的校验使用 java.security.MessageDigest类

转换字符集使用: Cp850

3.3.1 解析

01     import  org.apache.commons.codec.binary.Base64;

02 …...

03 final  String CHARSET = "Cp850" ;

04 byte [] dominoSecret = Base64.decodeBase64(ltpaDominoSecret.getBytes());

05 byte [] ltpa = Base64.decodeBase64(ltpaToken.getBytes());

06 ByteArrayInputStream stream = new  ByteArrayInputStream(ltpa);

07 int  usernameLength = ltpa.length – 40 ;

08 byte  header[] = new  byte [ 4 ];

09 byte  creation[] = new  byte [ 8 ];

10 byte  expires[] = new  byte [ 8 ];

11 byte  username[] = new  byte [usernameLength];

12 byte [] sha = new  byte [ 20 ];

13 // 读取LTPAToken版本号

14 stream.read(header, 0 , 4 );

15 if  (header[ 0 ] != 0  || header[ 1 ] != 1  || header[ 2 ] != 2 || header[ 3 ] != 3 )

16         throw  new  IllegalArgumentException( "Invalid ltpaToken format" );

17     // 读取开始时间

18    stream.read(creation, 0 , 8 );

19    // 读取到期时间

20    stream.read(expires, 0 , 8 );

21    // 读取Domino用户DN

22    stream.read(username, 0 , usernameLength);

23    // 读取SHA校验和

24    stream.read(sha, 0 , 20 );

25     // 转换用户名

26    char  characters[] = new  char [usernameLength];

27    try  {

28         InputStreamReader isr = new  InputStreamReader(

29             new  ByteArrayInputStream(username),

30             CHARSET);

31         isr.read(characters);

32     } catch  (Exception e) {

33     }

34     // 获得Domino用户DN

35     String dn = new  String(characters);

36     // 获得创建时间

37    Date creationDate = new  Date(

38         Long.parseLong( new  String(creation), 16 ) * 1000 );

39     // 获得到期时间

40    Date expiresDate = new  Date(

41         Long.parseLong( new  String(expires), 16 ) * 1000 );

42 …...

43 // 创建LTPA Token

44     ByteArrayOutputStream ostream = new  ByteArrayOutputStream();

45     try  {

46         // LTPA Token版本号

47         ostream.write(header);

48         // 创建时间

49         ostream.write(creation);

50         // 过期时间

51         ostream.write(expires);

52         // Domino用户DN,如CN=SquallZhong/O=DigiWin

53         ostream.write(username);

54         // Domino LTPA 密钥

55         ostream.write(dominoSecret);

56         ostream.close();

57     } catch  (IOException e) {

58         throw  new  RuntimeException(e);

59     }

60     // 进行 SHA-1 校验和

61     MessageDigest md;

62     try  {

63         md = MessageDigest.getInstance( "SHA-1" );

64         md.reset();

65     } catch  (NoSuchAlgorithmException e) {

66         throw  new  RuntimeException(e);

67     }

68     byte [] digest = md.digest(ostream.toByteArray());

69     // 完成 SHA-1 校验和,digest长度为20

70     boolean  valid = MessageDigest.isEqual(digest, sha);

3.3.2 生成

01     /**

02      * 为指定用户创建有效的LTPA Token.创建时间为now.

03      *

04      * @param username

05      *            - 用户名,注:使用用户全称,如:CN=SquallZhong/O=VGOLive Technology

06      * @param creationTime

07      *            - 创建时间

08      * @param durationMinutes

09      *            - 到期时间,单位:分钟

10 @param ltpaSecretStr

11      *            - Domino Ltpa 加密字符串

12      * @return - 返回已Base64编码的Ltpa Cookie.

13      * @throws NoSuchAlgorithmException

14      * @throws Base64DecodeException

15      */

16     public  static  String createLtpaToken(String username,

17             GregorianCalendar creationTime, int  durationMinutes,

18             String ltpaSecretStr) throws  NoSuchAlgorithmException {

19         // Base64解码ltpaSecretStr

20         byte [] ltpaSecret = Base64.decodeBase64(ltpaSecretStr.getBytes());

21         // 用户名字节数组

22         byte [] usernameArray = username.getBytes();

23         byte [] workingBuffer = new  byte [preUserDataLength

24                 + usernameArray.length + ltpaSecret.length];

25

26         // 设置ltpaToken版本至workingBuffer

27         System.arraycopy(ltpaTokenVersion, 0 , workingBuffer, 0 ,

28                 ltpaTokenVersion.length);

29         // 获得过期时间,过期时间=当前时间+到期时间(分钟)

30         GregorianCalendar expirationDate = (GregorianCalendar) creationTime

31                 .clone();

32         expirationDate.add(Calendar.MINUTE, durationMinutes);

33

34         // 转换创建时间至16进制字符串

35         String hex = dateStringFiller

36                 + Integer.toHexString(

37                         ( int ) (creationTime.getTimeInMillis() / 1000 ))

38                         .toUpperCase();

39         // 设置创建时间至workingBuffer

40         System.arraycopy(hex.getBytes(), hex.getBytes().length

41                 - dateStringLength, workingBuffer, creationDatePosition,

42                 dateStringLength);

43

44         // 转换过期时间至16进制字符串

45         hex = dateStringFiller

46                 + Integer.toHexString(

47                         ( int ) (expirationDate.getTimeInMillis() / 1000 ))

48                         .toUpperCase();

49         // 设置过期时间至workingBuffer

50         System.arraycopy(hex.getBytes(), hex.getBytes().length

51                 - dateStringLength, workingBuffer, expirationDatePosition,

52                 dateStringLength);

53

54         // 设置用户全称至workingBuffer

55         System.arraycopy(usernameArray, 0 , workingBuffer, preUserDataLength,

56                 usernameArray.length);

57

58         // 设置已Base64解码ltpaSecret至workingBuffer

59         System.arraycopy(ltpaSecret, 0 , workingBuffer, preUserDataLength

60                 + usernameArray.length, ltpaSecret.length);

61         // 创建Hash字符串

62         byte [] hash = createHash(workingBuffer);

63

64         // ltpaToken版本+开始时间(16进制)+到期时间(16进制)+用户全名+SHA-1(ltpaToken版本+开始时间(16进制)+到期时间(16进制)+用户全名)

65         byte [] outputBuffer = new  byte [preUserDataLength + usernameArray.length

66                 + hashLength];

67         System.arraycopy(workingBuffer, 0 , outputBuffer, 0 , preUserDataLength

68                 + usernameArray.length);

69         System.arraycopy(hash, 0 , outputBuffer, preUserDataLength

70                 + usernameArray.length, hashLength);

71         // 返回已Base64编码的outputBuffer

72         return  new  String(Base64.encodeBase64(outputBuffer));

73     }

74 …...

4. 通过F5 BIG-IP创建Domino LTPAToken

F5 iRule代码如下:

when RULE_INIT {

01 set  cookie_name "LtpaToken"            # 不更改

02  set  ltpa_version "\x00\x01\x02\x03"    # 不更改

03  set  ltpa_secret "b64encodedsecretkey"  # 从Domino SSO文档获得ltpa密钥

04  set  ltpa_timeout "1800"                # 从Domino SSO文档中获得过期时间,单位:秒

05 }

06

07 when HTTP_REQUEST {

08  #

09  # Do your usual F5 HTTP authentication here

10  #

11  # Initial values

12  set  creation_time_temp [clock seconds]

13  set  creation_time [ format  % X $creation_time_temp]

14  set  expr_time_temp [expr { $creation_time_temp +  $::ltpa_timeout}]

15  set  expr_time [ format  % X $expr_time_temp]

16  set  username [HTTP::username]

17  set  ltpa_secret_decode [b64decode $::ltpa_secret]

18  # First part of token

19  set  cookie_data_raw {}

20  append cookie_data_raw $::ltpa_version

21  append cookie_data_raw $creation_time

22  append cookie_data_raw $expr_time

23  append cookie_data_raw $username

24  append cookie_data_raw $ltpa_secret_decode

25  # SHA1 of first part of token

26  set  sha_cookie_raw [sha1 $cookie_data_raw]

27  # Final not yet encoded token

28  set  ltpa_token_raw {}

29  append ltpa_token_raw $::ltpa_version

30  append ltpa_token_raw $creation_time

31  append ltpa_token_raw $expr_time

32  append ltpa_token_raw $username

33  append ltpa_token_raw $sha_cookie_raw

34  # Final Base64 encoded token

35  set  ltpa_token_final [b64encode $ltpa_token_raw]

36  # Insert the cookie

37  HTTP::cookie insert name $::cookie_name value $ltpa_token_final

38  }

39  # Remove Authorization HTTP header to avoid using basic authentication

40  if  { [HTTP::header exists "Authorization" ] } {

41  HTTP::header remove "Authorization"

42  }

43 }

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

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

相关文章

fastapi 模式的额外信息,示例 / Cookie参数 / Header参数

文章目录1. Pydantic schema_extra2. Field 的附加参数3. Body 额外参数4. Cookie 参数5. Header 参数5.1 重复的 headerslearn from https://fastapi.tiangolo.com/zh/tutorial/schema-extra-example/ 添加一个将在文档中显示的 example 1. Pydantic schema_extra from typ…

Android(java)学习笔记171:服务(service)之绑定服务调用服务里面的方法

1.绑定服务调用服务里面的方法,图解: 步骤: (1)在Activity代码里面绑定 bindService(),以bind的方式开启服务 ; bindService(intent, new MyConn(), BIND_AUT…

LeetCode 1909. 删除一个元素使数组严格递增

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始的整数数组 nums ,如果 恰好 删除 一个 元素后,数组 严格递增 ,那么请你返回 true ,否则返回 false 。 如果数组本身已经是严格递增的,请你也返回 true 。 数组…

ATS push cache 测试

测试 ATS 注入缓存 参考了: http://serverfault.com/questions/471684/push-content-to-apache-traffic-servers-cache 得到返回:HTTP/1.0 400 Response Not Cachable 搜索得知,头部传入信息太少,必须包含反映时间的项。那个回答应…

fastapi 响应模型 / 响应状态码 / 表单参数

文章目录1. response_model2. 添加输出模型3. 响应模型编码参数4. response_model_include 和 response_model_exclude5. 代码复用:继承6. Union7. 任意 dict 的响应8. 响应状态码9. 表单参数learn from https://fastapi.tiangolo.com/zh/tutorial/response-model/ …

java对象内存模型_Java对象的内存模型

众所周知,函数调用在内存中是通过压栈,退栈实现的,而Java的方法调用则是在JVM栈中通过栈帧实现的,且所有的Java对象都只在堆上分配内存.那么一个Java对象在堆内存里到底长啥样呢?实际上,当一个对…

fastapi 请求文件 / 表单 / 处理错误 / 路径操作配置 / jsonable_encoder

文章目录1. File 参数2. 多文件上传3. 请求表单与文件4. 处理错误5. 自定义响应头6. 自定义异常处理器7. 覆盖默认异常处理器8. 使用 RequestValidationError 的请求体9. 复用 FastAPI 异常处理器10. 路径操作参数配置10.1 status_code,tags10.2 summary&#xff0c…

fastapi PUT更新数据 / PATCH部分更新

文章目录1. PUT 更新2. 用 PATCH 进行部分更新learn from https://fastapi.tiangolo.com/zh/tutorial/body-updates/1. PUT 更新 注意,put 没有指定的值,会被重置为默认值 from typing import List, Optionalfrom fastapi import FastAPI from fastap…

Chrome 扩展 最近的历史 HistoryBar v1.1

说明 以前用过一段时间傲游浏览器,渐渐的习惯了它的鼠标手势和一些细微的人性化的功能。比方地址栏左边的“近期訪问的页面”button。能够方便的找到近期 20 条历史记录。 但后来因为某些原因又回到了 Chrome 的怀抱,于是就没有了这些不起眼但非常好用的…

LeetCode 2047. 句子中的有效单词数

文章目录1. 题目2. 解题1. 题目 句子仅由小写字母(a 到 z)、数字(0 到 9)、连字符(-)、标点符号(!、. 和 ,)以及空格( )组成。 每个句子可以根据空格分解成 …

LeetCode 2048. 下一个更大的数值平衡数(枚举)

文章目录1. 题目2. 解题1. 题目 如果整数 x 满足:对于每个数位 d ,这个数位 恰好 在 x 中出现 d 次。 那么整数 x 就是一个 数值平衡数 。 给你一个整数 n ,请你返回 严格大于 n 的 最小数值平衡数 。 示例 1: 输入&#xff1a…

LeetCode 2049. 统计最高分的节点数目(DFS)

文章目录1. 题目2. 解题1. 题目 给你一棵根节点为 0 的 二叉树 ,它总共有 n 个节点,节点编号为 0 到 n - 1 。 同时给你一个下标从 0 开始的整数数组 parents 表示这棵树,其中 parents[i] 是节点 i 的父节点。 由于节点 0 是根,所…

python源文件编码的含义_【原创】Python 源文件编码解读

以下内容源于对 PEP-0263 的翻译和解读,同时给出了一些网上网友的说法。 我是分割线 PEP 0263 -- Defining Python Source Code Encodings【摘要】给出声明 Python 源文件编码的语法。该编码信息后续会被 Python 解析器用于解析源文件。这种方式增强了对源文件中 U…

MyEclipse中SVN的常见的使用方法

本次主要内容: 一 、导入项目 (Checkout)。从svn资源库检出 二 、更新 (Update) 三、锁(对要修改的文件加锁,防止文件冲突) 四、提交(项目修改后的提交) 五、…

fastapi 路径依赖项Depends / 装饰器依赖dependencies / 全局依赖 / 带 yield 的依赖

文章目录1. 依赖项2. 类作为依赖3. 子依赖项3.1 多次使用同一个依赖项4. 路径操作装饰器依赖项5. 全局依赖项6. 带 yield 的依赖项7. 使用带 yield 上下文管理器作为依赖项learn from https://fastapi.tiangolo.com/zh/tutorial/dependencies/ 1. 依赖项 只能传给 Depends 一…

fastapi 安全性 / APIRouter / BackgroundTasks / 元数据 / 测试调试

文章目录1. 例子2. 获取当前用户3. 使用密码和 Bearer 的简单 OAuth24. 使用(哈希)密码和 JWT Bearer 令牌的 OAuth25. 多个应用文件5.1 APIRouter6. BackgroundTasks7. 元数据7.1 标题、描述和版本7.2 openapi_tags 标签元数据7.3 OpenAPI URL7.4 文档 …

java json path_Java使用JSONPath解析JSON完整内容详解

JsonPath是一种简单的方法来提取给定JSON文档的部分内容。 JsonPath有许多编程语言,如Javascript,Python和PHP,Java。JsonPath提供的json解析非常强大,它提供了类似正则表达式的语法,基本上可以满足所有你想要获得的js…

LeetCode 2050. 并行课程 III(拓扑排序)

文章目录1. 题目2. 解题1. 题目 给你一个整数 n ,表示有 n 节课,课程编号从 1 到 n 。 同时给你一个二维整数数组 relations ,其中 relations[j] [prevCoursej, nextCoursej] ,表示课程 prevCoursej 必须在课程 nextCoursej 之前…

LeetCode 1521. 找到最接近目标值的函数值(位运算)

文章目录1. 题目2. 解题1. 题目 Winston 构造了一个如上所示的函数 func 。他有一个整数数组 arr 和一个整数 target ,他想找到让 |func(arr, l, r) - target| 最小的 l 和 r 。 请你返回 |func(arr, l, r) - target| 的最小值。 请注意, func 的输入参…

LeetCode 2053. 数组中第 K 个独一无二的字符串(哈希)

文章目录1. 题目2. 解题1. 题目 独一无二的字符串 指的是在一个数组中只出现过 一次 的字符串。 给你一个字符串数组 arr 和一个整数 k ,请你返回 arr 中第 k 个 独一无二的字符串 。 如果 少于 k 个独一无二的字符串,那么返回 空字符串 “” 。 注意…