IdentityServer4(六)授权码流程原理之SPA

在【One by One系列】IdentityServer4(四)授权码流程中提过一句:

为了安全,IdentityServer4是带有PKCE支持的授权码模式

我们来回顾一下授权码流程

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

--摘自阮一峰老师-理解OAuth 2.0,自认为阮老师这块已经写比较清晰了,正所谓”眼前有景道不得,崔颢题诗在上头“。

1.什么是PKCE

PKCE,全称Proof Key for Code Exchange,上篇讲到SPA,这是一种没有后端服务器的原生客户端,代码都在用户本地设备上运行,比如SPA在用户浏览器上运行,Win/Mac客户端,iOS/Android APP,如果让这些原生客户端安全地存放密钥(client secret)并不现实,且容易被破解。

  • Implicit Flow:我们没有介绍Implicit Flow,官方最新文档也没有,现在来看,可能就是因为安全的原因。Access Token会直接被传递给Redirect URL,容易被截取Access-Token

  • Authorization Code Flow:Redirect URL只会接收一个授权码,且授权码必须要和Client ID,Client Secret一同使用才能获取Access Token。然而原生客户端无法安全保存Client Secret,第三方恶意应用可以破解Client Secret,并按上述方法截取Authorization Code,同样不建议使用。

PKCE,旨在提高移动设备上授权代码流程执行过程中的安全性。有关该功能的定义,参阅RFC7636,微软翻译为保护授权码授权。实质是通过密码学技术手段,确保恶意第三方即使截获到授权码(Authorization Code)或者其他密钥,也无法向认证服务器交换获取Access Token。

PKCE要求所有客户端必须需要实现的内容:

  • 随机生成一串字符串,并用URL-Safe的Base64编码处理,结果为:code_verifier

  • code_verifier通过SHA256哈希加密,并用URL-Safe的Base64编码处理,结果为:code_challenge

//1.生成code_verifier
let code_verifier=generateRandomString(32);//2.生成code_challenge
let code_challenge=generateCodeChallenge(code_verifier);function generateRandomString(length) {let text = "";let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for (let i = 0; i < length; i++) {text += possible.charAt(Math.floor(Math.random() * possible.length));}return text;
}
function generateCodeChallenge(code_verifier) {return code_challenge = base64URL(CryptoJS.SHA256(code_verifier))
}

2.PKCE授权码流程

那么PKCE支持的授权码流程就发生了变化,具体流程如下:

  • (A)客户端除了response_type,Scope等标准参数,还必须带上,code_challengecode_challenge_method,发起对服务器的/authorize端点请求

  • (B)服务器对/authorize除了执行标准的OAuth请求验证,还会检查code_challengecode_challenge_method是否存在,并存储

  • (C)服务器返回Authorization Code

  • (D)客户端拿到Authorization Code,就用Authorization Codecode_verifier向服务端的/token端点发起请求,获取Access-token

  • (E)服务端对/token端点,除了执行标准的OAuth验证外,还会使用客户端传过来的code_verifier和服务端存储的code_challenge_method来生成自己的code_challenge

  • (F)服务端会将生成code_challenge与初始请求/authorize端点的code_challenge(并根据Authorization Code存储在服务端)两相比较

  • 匹配,颁发access-token

    • 不匹配,拒绝该请求

流程图

注意点:

  • 上图的t(code_verifier)指的就是code_challenge

  • code_verifier是一个加密字符串,其所具有的熵必须要足够大,使攻击者无法预测或猜到其值

任何截获到的中间方,并不能由code_challenge破解出code_verifier,这里只有客户端知道这两个值。所以截获到code_challengeAuthorization Code,也换不来想要的token,换来的只有拒绝。总而言之,这样降低恶意使用Authorization Code与Access-token的行为的风险。

3.查看IdentityServer4授权码流程

知晓了PKCE的男人,现在想对IdentityServer4授权码流程有一个更详细了了解,以及对PKCE的验证,我们使用WireShark对整个请求进行抓包。

由于fiddler智能抓包一些浏览器请求,对于整个过程中的一些后台请求,并不能捕获,所以我们选择WireShark这个工具。也不能通过浏览器开发者模式,由于授权重定向的过程太快,好多请求都看不清。

3.1 运行IdentityServer

cd .\IdentityServer\
dotnet run

3.2 运行wireshark

打开软件,选择-adapter for loopback traffic capture开始抓包

3.2 运行JavaScript客户端

cd .\JavaScript\
dotnet run

3.2 操作

按照上一篇文章操作,登录,注销

3.3 停止wireshark的捕获

停止捕获,通过自带的列表过滤报文,如下图

这就是整个授权登录,然后注销登录过程中,发生的http请求。

4.详解IdentityServer4授权码流程(SPA)

4.1 请求IdentityServer4的配置端点-获取authorize端点

请求

...
GET /.well-known/openid-configuration HTTP/1.1\r\n
Host: localhost:5001\r\n
...

响应

200 OK
Date: Sun, 12 Jul 2020 17:38:32 GMT
Content-Type: application/json; charset=UTF-8
{"issuer":"http://localhost:5001","jwks_uri":"http://localhost:5001/.well-known/openid-configuration/jwks","authorization_endpoint":"http://localhost:5001/connect/authorize","token_endpoint":"http://localhost:5001/connect/token","userinfo_endpoint":"http://localhost:5001/connect/userinfo","end_session_endpoint":"http://localhost:5001/connect/endsession","check_session_iframe":"http://localhost:5001/connect/checksession","revocation_endpoint":"http://localhost:5001/connect/revocation","introspection_endpoint":"http://localhost:5001/connect/introspect","device_authorization_endpoint":"http://localhost:5001/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["openid","profile","api1","offline_access"],"claims_supported":["sub","name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at"],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","password","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}

这里主要是获取授权端点,由oidc-client.js内部触发。

4.2 请求authorize端点

请求

GET /connect/authorize?client_id=js&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&response_type=code&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&code_challenge=kz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E&code_challenge_method=S256&response_mode=query HTTP/1.1
Host: localhost:5001

响应

Status Code: 302
Location: http://localhost:5001/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery

请求授权端点,带上了response_type,scope,profile,code_challenge,code_challenge_method,响应302重定向,

4.3 跳转登录页

请求

GET /Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery HTTP/1.1
Host: localhost:5001

响应

Status Code: 200
HTTP/1.1 200 OK
<!DOCTYPE html>
<html lang="en">
</html>

重定向至登录页,响应登录页html

4.4 登录操作

请求

POST /Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c
POST /Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery HTTP/1.1
Host: localhost:5001
Content-Type: application/x-www-form-urlencoded
ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery&Username=admin&Password=admin123456%21&button=login&__RequestVerificationToken=CfDJ8EX5EDLzf1lFsdLD-61W3Pr-hyJIt5MjRQdemuM-9ab4QyRWG2omEwKHp0SZhc2a_ZjsoJEzge4QvrP_zVvkfQHq5bAki5KFTo1upo251HiQvnsFMp4ptx0KnmoHxBbIjDhBQ2VRh4fQCyJJiM791Nk&RememberLogin=false

响应

Status Code: 302
Location: /connect/authorize/callback?client_id=js&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&response_type=code&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&code_challenge=kz0v3GRm8yFhm
Set-Cookie: idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; path=/; samesite=none\r\n

登录操作,在原有的参数基础上,增加Username,Password,Post提交,响应302重定向,并Set-Cookie

4.5 验证授权,回调返回授权码

请求

GET /connect/authorize/callback?client_id=js&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&response_type=code&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&code_challenge=kz0v3GRm8yFhmjoOYBWX2pg68N5waBd0
Host: localhost:5001\r\n
Cookie:Cookie pair: idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803

响应

Status Code: 302
Location: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-dit
Set-Cookie: idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ; path=/; samesite=none; httponly

验证用户名、密码,服务端cookie已设置,验证成功,再次响应302,重定向客户端页面callback.html,并Set-Cookie

4.6 请求IdentityServer4的配置端点-获取token端点

请求

GET /.well-known/openid-configuration HTTP/1.1
Host: localhost:5001
Sec-Fetch-Mode: cors
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F

响应

200 OK
Date: Sun, 12 Jul 2020 17:38:32 GMT
Content-Type: application/json; charset=UTF-8
{"issuer":"http://localhost:5001","jwks_uri":"http://localhost:5001/.well-known/openid-configuration/jwks","authorization_endpoint":"http://localhost:5001/connect/authorize","token_endpoint":"http://localhost:5001/connect/token","userinfo_endpoint":"http://localhost:5001/connect/userinfo","end_session_endpoint":"http://localhost:5001/connect/endsession","check_session_iframe":"http://localhost:5001/connect/checksession","revocation_endpoint":"http://localhost:5001/connect/revocation","introspection_endpoint":"http://localhost:5001/connect/introspect","device_authorization_endpoint":"http://localhost:5001/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["openid","profile","api1","offline_access"],"claims_supported":["sub","name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at"],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","password","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}

获取token端点值

4.7 请求token端点

请求

POST /connect/token HTTP/1.1
Host: localhost:5001
Content-Type: application/x-www-form-urlencoded
Sec-Fetch-Mode: cors
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8client_id=js&code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&code_verifier=e661671994bd4eb89bb9e4da214e34f0970c24c9a36e41fe9e05aec433c604c2f721da29216345b0898445e5e105c86f&grant_type=authorization_code

响应

Status Code: 200
{"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3NTgxMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiYXVkIjoianMiLCJpYXQiOjE1OTQ1NzU1MTIsImF0X2hhc2giOiJhaU9rX05xeDh0OERXeTc5cFo3WFhBIiwic19oYXNoIjoiWkllczRfaldVMEFBaTI0ZUY4Yjg1dyIsInNpZCI6IjNDRUFCQkI2MkJFMUY2REREN0I3OTNBOEY1QkYxODAzIiwic3ViIjoiMSIsImF1dGhfdGltZSI6MTU5NDU3NTUxMiwiaWRwIjoibG9jYWwiLCJhbXIiOlsicHdkIl19.VmdopUrWH4rcrJp3nXcE-LLNodowQh6n0-HjN_aZZNOj1xPECwG_g0-nY9N9q-jNeapkIrWk2U2Y9liUXuBAOLHhT0Txou4dNhAdMIvYJ8SKRSgh06SEbpGT_hDtN345YZd9IJSjGWo3q_B04p03pw6S92tQp6ae74v1mMAgCskVnKth0SWpPqUPSuZjSdlcuzhA7OvXlz3wmeGJPu5c0jC1BBzrrM1_WmFvUCmwCo9q3Z0MLcz6eq1JZafhSkkRSAgTJdWIdrq6w7Yj1DInETebOhJrt3Yl7jGVAjJqK1WMnJym3J4n9d5GYfv9wA4eu3GgKvG_rax1GjgtV3zR0g","access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3OTExMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiY2xpZW50X2lkIjoianMiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTk0NTc1NTEyLCJpZHAiOiJsb2NhbCIsImp0aSI6IkU3OTdDOTI0QUJFOTI1QTQ3N0Y1NDVCQUVFRkRGMUE3Iiwic2lkIjoiM0NFQUJCQjYyQkUxRjZEREQ3Qjc5M0E4RjVCRjE4MDMiLCJpYXQiOjE1OTQ1NzU1MTIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJhcGkxIl0sImFtciI6WyJwd2QiXX0.Cl4u3bU9bm0mG9qjn52WwstbPmuhBetKkEIRgVENIU_4hurJ8fRPiNc3zzl0tzgIw0_yvHy8eyA6EyVfSMyQZ77ao0TjEkBcTu62H7eKTHWrdKyp0eEhcxRiVvAYAQcFP2NPva8z0zZiVUUnE0q6-WiE7P_hDF8Ljs6AyYAS4khWX9iG-WoSlqDlalOo7ohU7gGleIpnlH5LUvQpkDVHbOzCNviJH6r4VbqT7llnIDNNjMwy9cwh3TJcYNsFZjTL3jsQtmbNv9ajmNBZKhWvGSRN_6ywgbPcL54FEqVTe3hfwdcSjHCSV2Owzcu6at8UplAm-Kd0T09ay6ChXWz67g","expires_in": 3600,"token_type": "Bearer","scope": "openid profile api1"
}

codescopeclient_idcode_verifiergrant_type等必要参数作为post参数请求token端点,返回id_tokenaccess_tokenexpires_inscope等.

4.8 请求userinfo端点

预检请求

OPTIONS /connect/userinfo HTTP/1.1
Host: localhost:5001
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F

响应

Status Code: 204
Access-Control-Allow-Headers: authorization\r\n
Access-Control-Allow-Methods: GET\r\n
Access-Control-Allow-Origin: http://localhost:6003\r\n

正式请求

Host: localhost:5001
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3OTExMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiY2xpZW50X2lkIjoianMiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTk0NTc1NTEyLCJpZHAiOiJsb2NhbCIsImp0aSI6IkU3OTdDOTI0QUJFOTI1QTQ3N0Y1NDVCQUVFRkRGMUE3Iiwic2lkIjoiM0NFQUJCQjYyQkUxRjZEREQ3Qjc5M0E4RjVCRjE4MDMiLCJpYXQiOjE1OTQ1NzU1MTIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJhcGkxIl0sImFtciI6WyJwd2QiXX0.Cl4u3bU9bm0mG9qjn52WwstbPmuhBetKkEIRgVENIU_4hurJ8fRPiNc3zzl0tzgIw0_yvHy8eyA6EyVfSMyQZ77ao0TjEkBcTu62H7eKTHWrdKyp0eEhcxRiVvAYAQcFP2NPva8z0zZiVUUnE0q6-WiE7P_hDF8Ljs6AyYAS4khWX9iG-WoSlqDlalOo7ohU7gGleIpnlH5LUvQpkDVHbOzCNviJH6r4VbqT7llnIDNNjMwy9cwh3TJcYNsFZjTL3jsQtmbNv9ajmNBZKhWvGSRN_6ywgbPcL54FEqVTe3hfwdcSjHCSV2Owzcu6at8UplAm-Kd0T09ay6ChXWz67g
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8

响应

Status Code: 200
{"name": "RandyField","given_name": "Randy","family_name": ["Field","Randy"],"website": "http://www.randyfield.cn","sub": "1"
}

4.9 请求checksession端点

请求

GET /connect/checksession HTTP/1.1
Host: localhost:5001
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w; idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ

响应

Status Code: 200
Content-Type: text/html; charset=UTF-8<!DOCTYPE html>
<!--Copyright (c) Brock Allen & Dominick Baier. All rights reserved.-->
<!--Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.-->
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<title>Check Session IFrame</title>
</head>
<body>
</body>
</html>

之后又请求了一次/.well-known/openid-configuration,/connect/checksession

第一次与第二次/connect/checksession几乎一样的请求与响应,唯一的差别

  • Referer不同

    • 第一次:http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F

    • 第二次:http://localhost:6003/index.html

从结果推导过程,第一次,是回调页面callback.html,请求了/connect/checksession,获取了一个iframe的html,callback.html中window.location = "index.html";由会重定向到index.html,可以推导出,这里是帮index.html请求渲染内容,因为index中有一个隐藏的iframe,如图

两次checksession之间还有一次/.well-known/openid-configuration请求,这次Referer已经是index.html,说明已经发生重定向了,重定向之后,由于document中有这个iframe,自然就会再发起一次请求,所以这两次的Referer参数不一样。

4.10 注销登录操作

①.请求endsession端点

请求
GET /connect/endsession?id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3NTgxMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiYXVkIjoianMiLCJpYXQiOjE1OTQ1NzU1MTIsImF0X2hhc2giOiJhaU9rX05xeDh0OERXeTc5cFo3WFhBIiwic19oYXNoIjoiWkllczRfaldVMEFBaTI0ZUY4Yjg1dyIsInNpZCI6IjNDRUFCQkI2MkJFMUY2REREN0I3OTNBOEY1QkYxODAzIiwic3ViIjoiMSIsImF1dGhfdGltZSI6MTU5NDU3NTUxMiwiaWRwIjoibG9jYWwiLCJhbXIiOlsicHdkIl19.VmdopUrWH4rcrJp3nXcE-LLNodowQh6n0-HjN_aZZNOj1xPECwG_g0-nY9N9q-jNeapkIrWk2U2Y9liUXuBAOLHhT0Txou4dNhAdMIvYJ8SKRSgh06SEbpGT_hDtN345YZd9IJSjGWo3q_B04p03pw6S92tQp6ae74v1mMAgCskVnKth0SWpPqUPSuZjSdlcuzhA7OvXlz3wmeGJPu5c0jC1BBzrrM1_WmFvUCmwCo9q3Z0MLcz6eq1JZafhSkkRSAgTJdWIdrq6w7Yj1DInETebOhJrt3Yl7jGVAjJqK1WMnJym3J4n9d5GYfv9wA4eu3GgKvG_rax1GjgtV3zR0g&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Findex.html 
Host: localhost:5001
Connection: keep-alive
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w; idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ
响应
Status Code: 302
Location: http://localhost:5001/Account/Logout?logoutId=CfDJ8EX5EDLzf1lFsdLD-61W3Pq-tO1b1UEAB4vvmYhY4sokyl8em0HlVYGX9otqLmnglBnG6V_RukkVflCbku5Elb72VsJkntUDrh6G1AP1ctnkCWhiuN9lIBouTf9swekyYrj8H0Q-5iHISwsYmXz00kEPqWR1-7-DoXbv2g3Dxtt3fPxVN5WmFd0-I7zuLoyPrpiqz62TYGUNygB1qOt0BXsvwVLWyl_amuMVbqUgJkvkbS4049YYVK7W0fl55L66mDnBEF5ktdixHE9ld3_dso-4FL8ppa-wdUq9Wy6JPo5p1S4BIf_LCfX0Cp4eDVow5sgVtpPfbamRX-pRHco_-H8jifrDg5xVmxupY6NAMgzK8Sbn6lZhW_KjkkpTyWRiE7kfc_uaYAeykfDGqvmAfGq7-9suIwGd3vvJw2-pn6aVuzTH4o3SycophXWv-5BUYQ

请求服务端endsession端点,传递参数id-token,响应302重定向注销登录页面。

②.请求Logout页面

请求
GET /Account/Logout?logoutId=CfDJ8EX5EDLzf1lFsdLD-61W3Pq-tO1b1UEAB4vvmYhY4sokyl8em0HlVYGX9otqLmnglBnG6V_RukkVflCbku5Elb72VsJkntUDrh6G1AP1ctnkCWhiuN9lIBouTf9swekyYrj8H0Q-5iHISwsYmXz00kEPqWR1-7-DoXbv2g3Dxtt3fPxVN5WmFd0-I7zuLoyPrpiqz62TYGUNygB1qOt0BXsvwVLWyl_amuMVbqUgJkvkbS4049YYVK7W0fl55L66mDnBEF5ktdixHE9ld3_dso-4FL8ppa-wdUq9Wy6JPo5p1S4BIf_LCfX0Cp4eDVow5sgVtpPfbamRX-pRHco_-H8jifrDg5xVmxupY6NAMgzK8Sbn6lZhW_KjkkpTyWRiE7kfc_uaYAeykfDGqvmAfGq7-9suIwGd3vvJw2-pn6aVuzTH4o3SycophXWv-5BUYQ HTTP/1.1
Host: localhost:5001
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w; idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ
响应
Status Code: 200
Content-Type: text/html; charset=utf-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: idsrv.session=.; expires=Fri, 12 Jul 2019 17:38:40 GMT; path=/; samesite=none
Set-Cookie: idsrv=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=none; httponly
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" /><title>IdentityServer4</title>  <link rel="icon" type="image/x-icon" href="/favicon.ico" /><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /><link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" /><link rel="stylesheet" href="/css/site.css" />
</head>
<body>  
<div class="nav-page"><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><a href="/" class="navbar-brand"><img src="/icon.png" class="icon-banner">IdentityServer4</a> </nav>
</div><div class="container body-container">      
<div class="logged-out-page"><h1>Logout<small>You are now logged out</small></h1><div>Click <a class="PostLogoutRedirectUri" href="http://localhost:6003/index.html">here</a> to return to the<span>JavaScript Client</span> application.</div><iframe width="0" height="0" class="signout" src="http://localhost:5001/connect/endsession/callback?endSessionId=CfDJ8EX5EDLzf1lFsdLD-61W3Ppjdh7zkU7fvaRvtKK_djZ_wTALkKC4YyDjpZmbnIsfrQa2BTfaRz1AkiCNlJLVYT2mUZA0Os9WyOBB1QDhYOjscWlomm6RWpUDy4L8tjr1mTdkH0T5IXOUsF7wcRrP-6ssERsPyswxqg9bFwkjuYlZnQYZOFSdFwGV8T3uru7BVhP8HywA6JeyYdUC-CQl_02vdiN3h5_bfxcP6sSbuu0kq8Dvs1GRXZT2B813Wy7DI_fmnWwyMiy2isjdpZjxx2E"></iframe>
</div></div><script src="/lib/jquery/dist/jquery.slim.min.js"></script><script src="/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

重定向至Logout页面,并通过Set-CookieIndentityServer-localhost:5001的cookie置为空,且响应一段包含隐藏src="http://localhost:5001/connect/endsession/callback"的iframe的html

③.触发endsession回调

请求
GET /connect/endsession/callback?endSessionId=CfDJ8EX5EDLzf1lFsdLD-61W3Ppjdh7zkU7fvaRvtKK_djZ_wTALkKC4YyDjpZmbnIsfrQa2BTfaRz1AkiCNlJLVYT2mUZA0Os9WyOBB1QDhYOjscWlomm6RWpUDy4L8tjr1mTdkH0T5IXOUsF7wcRrP-6ssERsPyswxqg9bFwkjuYlZnQYZOFSdFwGV8T3uru7BVhP8HywA6JeyYdUC-CQl_02vdiN3h5_bfxcP6sSbuu0kq8Dvs1GRXZT2B813Wy7DI_fmnWwyMiy2isjdpZjxx2E HTTP/1.1
Host: localhost:5001
Sec-Fetch-Dest: iframe
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w
响应
Status Code: 200
Date: Sun, 12 Jul 2020 17:38:39 GMT
Content-Type: text/html; charset=UTF-8
Server: Kestrel
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache
Transfer-Encoding: chunked
Content-Security-Policy: default-src 'none'; style-src 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY='
X-Content-Security-Policy: default-src 'none'; style-src 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY='
<!DOCTYPE html><html><style>iframe{display:none;width:0;height:0;}</style><body></body></html>

触发回调,cookie已置为空

5.总结

这里面确实是PKCE的授权码模式,其次整个客户端(SPA)与服务端交互过程有很多骚操作,比如在html里面返回一段隐藏的iframe,从而触发回调。下一篇,我们将会继续讨论在MVC应用中的IdentityServer4授权码流程,同样是PKCE,但是同样具有一些奇技淫巧的骚操作,待你我共赏。

对了,有个小贴士,网易有道词典跟WireShark有冲突,打开就是未响应,使用时,关闭有道词典即可,这个坑简直了,真是王大锤的万万没想到。

参考链接

https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

https://www.zhihu.com/question/31896659

https://tonyxu.io/zh/posts/2018/oauth2-pkce-flow/

https://www.cnblogs.com/hubwang2020/p/12671712.html

长按二维码关注

点外卖,先领券

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

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

相关文章

适合手机端的ckeditor样式_抖音运营干货(三):9款手机视频剪辑APP,让你轻松玩转后期!...

很多朋友想开始用手机拍视频&#xff0c;可能不知道如何剪辑&#xff01;本文将给大家介绍几款好用又方便的手机剪辑短视频工具&#xff0c;即便是零基础&#xff0c;用下面这些工具&#xff0c;你也可以轻松开始剪辑短视频。选择一款实用好用的剪辑工具很重要&#xff0c;工具…

来吧,是时候升级您的领英技术档案了

阅读此文需要2分钟&#xff08;文末有惊喜&#xff09;LinkedIn的应用之广超乎你的想象&#xff0c;包括社会招聘、公关、社群建设、销售、社交媒体营销&#xff08;包括社交广告&#xff09;以及员工宣传。LinkedIn档案不是一份简历&#xff0c;而是集客式营销&#xff08;inb…

c语言查单词小程序,【附源码】小程序初窥之简单查单词

新年假期百无聊赖&#xff0c;于是就看了一下微信小程序的开发方法&#xff0c;花了两天时间入了个门&#xff0c;这里记录一下。阅读之前&#xff0c;先确定你知道基本的 htmlcssjs 语法&#xff0c;这样就能更好地和我一样&#xff0c;以一个新手的视角来理解小程序。目标目标…

python连接mysql_Python爬虫进阶教程(八):MySQL 数据库连接

PyMySQL介绍PyMySql包含一个纯python的MySQL客户端库。PyMySQL的目标是成为MySQLdb的替代品&#xff0c;并在CPython、PyPy和IronPython上工作。版本要求python 下列之一CPython > 2.6 or > 3.3PyPy > 4.0IronPython 2.7mysql 下列之一MySQL > 4.1 (tested with on…

使用 gRPCurl 调试.NET 5的gPRC服务

介绍你用过 Curl 吗&#xff1f;这个工具允许你通过 http 来发送数据&#xff0c;现在有一个适用于gGRPC的工具&#xff0c;gRPCurl&#xff0c;在本文中&#xff0c;我将介绍如何下载安装这个工具&#xff0c;然后通过这个工具调试我们.NET 5上面的gGRC程序。安装 gRPCurlgRPC…

此 sqltransaction 已完成;它再也无法使用_手把手教你如何修眉毛,学会再也不用花钱去美容院了...

最近看到有网友在评论中询问怎么修眉毛&#xff0c;刚好小编我对修眉有一点研究&#xff0c;可能比不上专业修眉的&#xff0c;但是最少能看不是&#xff0c;今天拿出来献给大家&#xff0c;希望各位自己学会如何修眉毛&#xff0c;那样的话以后就再也不用花钱去美容院了。在此…

国产OS推广应从娃娃和体制内双管齐下

一直以来&#xff0c;国内桌面操作系统被微软垄断。究其根源&#xff0c;既有微软技术更成熟&#xff0c;软件生态丰富、服务完善、商业化水平更好等因素之外&#xff0c;也有老百姓习惯于使用Windows等因素。老百姓之所以习惯于Windows&#xff0c;则是因为破解版横行和微软早…

c语言铁路托运行李费用图,3.为铁路部门编写计算运费的程序。假设铁路托运行李,规定每张客票托运费计算方法是:行李重量不超过50kg...

满意答案wodfsdfeqd81推荐于 2018.10.08采纳率&#xff1a;51% 等级&#xff1a;12已帮助&#xff1a;5850人using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace ConsoleApplication40{class Program{static void Main(string[]…

ufo帧率测试网站_一加7游戏续航测试 满电开始玩猜猜能玩多久

业界顶级的90Hz刷新率屏幕打造沉浸式视觉体验&#xff1b;骁龙855移动平台加强性能输出&#xff1b;更加炫酷的曲面屏设计...采用无刘海式真全面屏&#xff0c;屏占比屏幕赠大的同时&#xff0c;电池容量也进一步得到提升。对于省电优化能力卓尔不群的一加7 Pro&#xff0c;让手…

并行模型Actor

并行开发时经常需要关注加锁和原子操作等一系列线程问题&#xff0c;而Actor模型内部状态由它自己维护&#xff0c;内部数据只能自己修改&#xff0c;因此Actor不需要过多关注线程问题。Actor模型Actor由状态&#xff08;State&#xff09;、邮箱&#xff08;Mailbox&#xff0…

linux cache fs,新闻|Linux 上将出现一个新的文件系统:bcachefs

这个有 5 年历史&#xff0c;由 Kent Oberstreet 创建&#xff0c;过去属于谷歌的文件系统&#xff0c;最近完成了全部关键组件。Bcachefs 文件系统自称其性能和稳定性与 ext4 和 xfs 相同&#xff0c;而其他方面的功能又可以与 btrfs 和 zfs 相媲美。主要特性包括校验、压缩、…

cad单位_CAD制图初学入门常用技巧汇总,CAD零基础也不怕!

CAD制图初学入门的小萌新们&#xff0c;最苦恼的莫过于&#xff1a;千辛万苦&#xff0c;好不容易安装的CAD软件&#xff0c;在实际CAD设计绘图中&#xff0c;居然遭遇各种问题。没有CAD制图初学入门教程、缺乏CAD大神指点、CAD图纸资源极度匮乏&#xff0c;CAD小萌新们的成长之…

跟本菜菜一起去体验Microsoft Windows Server Code Name Longhorn Server Core

消失的菜菜又回来了,这段时间由于很多原因没及时更新blog,忘大家见谅…还记得菜菜以前给大家介绍的Microsoft Windows Server Longhorn吗?今天我给大家带来Microsoft Windows Server Code Name "Longhorn” server core.也许很多人都听说过吧?!我在简单的给大家做个简单…

乐视android版本点四下,EUI5.9+Android7.0刷机包

乐视EUI5.9系统刷机包&#xff0c;乐视更新了EUI5.9&#xff0c;不仅有语音助手还有iPhone的小白点悬浮球&#xff0c;功能齐全&#xff0c;界面简单&#xff0c;实在是应该给程序员点个赞&#xff01;乐视EUI5.9系统刷机包更新内容&#xff1a;设置 新增悬浮球功能管家 新增应…

得出来的视差图左边有黑色补上原图_掌握这10个抠图方法,帮你轻松应对大部分抠图工作!(动图演示...

新手只要掌握今天这10个方法&#xff0c;就足够对付绝大部分的抠图工作了。1、橡皮擦工具橡皮擦工具&#xff0c;更多时候跟“抠图”看似没啥关系&#xff0c;然而&#xff0c;竟然它能起到“擦除”的作用&#xff0c;那么就完全可以用来抠图去背了&#xff0c;它的键盘快捷键是…

win10右键一直转圈_Win10电脑开机一直转圈无法进入系统的解决方法

相信很多用户在安装Win10系统之后&#xff0c;常常会出现一些问题&#xff0c;其中就有电脑开不了机一直在转圈的情况&#xff0c;那么遇到电脑开机一直在转圈怎么办呢&#xff1f;下面笔者就针对这一情况和大家介绍Win10系统电脑开不了机一直在转圈的解决方法。解决方法开机马…

跨平台导PDF,结合wkhtmltopdf很顺手

前言好东西要分享&#xff0c;之前一直在使用wkhtmltopdf进行pdf文件的生成&#xff0c;常用的方式就是先安装wkhtmltopdf&#xff0c;然后在程序中用命令的方式将对应的html生成pdf文件&#xff0c;简单而且方便&#xff1b;但重复的编码使得想在wkhtmltopdf基础上进行封装&am…

ueditor如何设置上传图片的高度宽度_怎么设置天猫主图

天猫主图很多人都设置过&#xff0c;不过&#xff0c;新手第一长主图往往不是很在行&#xff0c;所以也不懂怎么设置&#xff1f;那今天&#xff0c;麦顶电商就给大家科普下关于天猫长主图的知识&#xff0c;想要学习设置长主图的店主&#xff0c;这期的内容也正好适合你们。用…

鸿蒙系统2020正式版,鸿蒙2.0来了!华为开发者大会HDC 2020宣布

原标题&#xff1a;鸿蒙2.0来了&#xff01;华为开发者大会HDC 2020宣布华为官方宣布&#xff0c;2020年度华为开发者大会“HDC Together”将于9月10日至9月12日在东莞松山湖举办&#xff0c;早鸟门票明天起开售。根据预告&#xff0c;本次大会将带来主题演讲、技术论坛、行业大…

洋哥!我要进大厂!

阅读本文大概需要4分钟。最近不少读者咨询洋哥如何进大厂&#xff0c;回答的多了就想着要不整理出一个系列文章出来。说干就干&#xff0c;第一期先写写应届毕业和刚工作不久的计算机专业的童鞋如何进大厂。先说说必须要做到的几件事&#xff1a;第一&#xff0c;学校内的课程&…