Bee
(( ⩌ ˰ ⩌))
1
是否想试试给自己服务接上始皇论坛的认证,却因为没接触过而无从下手?
轻松动手,不用工具,使用 curl 就能尝尝成功的滋味,赶紧来试试吧!
- 取得 code
这边就拿始皇这篇的测试 client id 来使用。
直接打开始皇范例的 url,同意授权之后,导向 localhost ,没有架设服务的你,当然是打不开的。但是你可以手动从 url
取得你的 code
了
http://localhost:8181/oauth2/callback?code=democode&state=ttt1
- 有了 code 之后呢,就可以从
https://connect.linux.do/oauth2/token
接口领取 refresh token
跟 access token
。搞过 gpt 的佬友们想必对这两个名词都有 PTSD 或 c号戒断症 了。没错,就是熟悉的老朋友,access 有较短的有效期,而 refresh 则能刷新后者。
在 token 接口里,我们需要把 client id 跟 secret 中间以冒号间隔,编码成base64,然后附在 header 里。
始皇文章提供的测试用数值举例:hi3geJYfTotoiR5S62u3rh4W5tSeC5UG:VMPBVoAfOB5ojkGXRDEtzvDhRLENHpaN
你可以使用 windows 内建的编码工具
certutil -f -encode "C:\输入.txt" "C:\输出.txt"
或者你懒得创 txt 打指令,使用线上工具
我们就能得到结果为:
aGkzZ2VKWWZUb3RvaVI1UzYydTNyaDRXNXRTZUM1VUc6Vk1QQlZvQWZPQjVvamtHWFJERXR6dkRoUkxFTkhwYU4=
带进 header 里,就能向 token 请求:
curl -X POST -H "Authorization: Basic aGkzZ2VKWWZUb3RvaVI1UzYydTNyaDRXNXRTZUM1VUc6Vk1QQlZvQWZPQjVvamtHWFJERXR6dkRoUkxFTkhwYU4=" -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=authorization_code&code=你的代码&redirect_uri=http://localhost:8181/oauth2/callback" https://connect.linux.do/oauth2/token
返回例:
{"access_token":"eyJhb_A","expires_in":3600,"refresh_token":"e187zHrR7dPyzJ","token_type":"bearer"}
至此我们就能见到熟悉的老朋友了!
- 使用 access token 取得资料
curl -H "Authorization: Bearer eyJhb_A" https://connect.linux.do/api/user
返回例:
{
"id": 124,
"username": "Bee",
"name": "( ⩌ ˰ ⩌)",
"active": true,
"trust_level": 2,
"silenced": false
}
这样你的程序就能认证当前使用者的个人资料了。
在某些情况下,username
是可以被修改的,所以以 id
来辨别是比较好的方式。
- 那如果过期了怎么办?
我们把刚刚向 token 请求的内容改一下,grant_type=refresh_token
。
curl -X POST -H "Authorization: Basic aGkzZ2VKWWZUb3RvaVI1UzYydTNyaDRXNXRTZUM1VUc6Vk1QQlZvQWZPQjVvamtHWFJERXR6dkRoUkxFTkhwYU4=" -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=refresh_token&refresh_token=e187zHrR7dPyzJ" https://connect.linux.do/oauth2/token
返回例:
{"access_token":"eyJ403ioE","expires_in":3600,"token_type":"bearer"}
更新其他语言参考 ( ⩌ ˰ ⩌)
35 个赞
Hua
(Hua)
6
贴一版java的回调后的处理demo
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Base64;
@RestController
@RequestMapping("/oauth2")
public class OAuth2Controller {
private static final String TOKEN_ENDPOINT = "https://connect.linux.do/oauth2/token";
private static final String CLIENT_ID = "hi3geJYfTotoiR5S62u3rh4W5tSeC5UG";
private static final String CLIENT_SECRET = "VMPBVoAfOB5ojkGXRDEtzvDhRLENHpaN";
private static final String REDIRECT_URI = "http://localhost:8181/oauth2/callback";
@GetMapping("/callback")
public void callback(String code) throws IOException {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(TOKEN_ENDPOINT);
String credentials = CLIENT_ID + ":" + CLIENT_SECRET;
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
httpPost.setHeader("Authorization", "Basic " + encodedCredentials);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
httpPost.setHeader("Accept", "application/json");
StringEntity entity = new StringEntity(
"grant_type=authorization_code&" +
"code=" + code + "&" +
"redirect_uri=" + REDIRECT_URI,
"UTF-8");
httpPost.setEntity(entity);
String response = EntityUtils.toString(client.execute(httpPost).getEntity());
System.out.println("Response: " + response);
}
}
}
2 个赞
neo
(Neo)
7
拉起授权(也叫生成登录链接)要生成state,写进session,以便callback的时候跟回来的state做比对,避免攻击。
2 个赞
Hua
(Hua)
9
生成和发送state
参数
import javax.servlet.http.HttpSession;
import java.security.SecureRandom;
import java.math.BigInteger;
public class OAuth2InitiateAuthServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 生成随机state值并存储到会话中
HttpSession session = request.getSession();
String state = new BigInteger(130, new SecureRandom()).toString(32);
session.setAttribute("oauthState", state);
String authorizationRequestUrl = "https://connect.linux.do/oauth2/authorize" +
"?client_id=" + CLIENT_ID +
"&response_type=code" +
"&redirect_uri=" + REDIRECT_URI +
"&state=" + state;
response.sendRedirect(authorizationRequestUrl);
}
}
接收授权码和state
参数并验证
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class OAuth2CallbackServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 在请求中获取返回的state参数和授权码
String returnedState = request.getParameter("state");
String authorizationCode = request.getParameter("code");
HttpSession session = request.getSession();
String originalState = (String) session.getAttribute("oauthState");
// 验证state值
if (returnedState != null && returnedState.equals(originalState)) {
// state匹配 则继续处理 将上面的获取rt和at的逻辑放到这
} else {
// state不匹配 则拒绝请求
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
1 个赞
Hua
(Hua)
11
补一个请求用户信息的
其实大概跟获取token的是一样的
这里简单写一下
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class OAuth2TokenRequest {
public static void main(String[] args) {
// OAuth2 令牌端点
String tokenEndpoint = "https://connect.linux.do/api/user";
// 客户端认证信息的Base64编码值 就是回调后的处理demo中的encodedCredentials
String encodedCredentials = "aGkzZ2VKWWZUb3RvaVI1UzYydTNyaDRXNXRTZUM1VUc6Vk1QQlZvQWZPQjVvamtHWFJERXR6dkRoUkxFTkhwYU4=";
// 授权码
String authorizationCode = "接收到的授权码"; // 这里替换为实际的授权码
// 重定向URI
String redirectUri = "http://localhost:8181/oauth2/callback";
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(tokenEndpoint);
// 设置HTTP头信息
httpPost.setHeader("Authorization", "Basic " + encodedCredentials);
// 执行请求并获取响应
String responseString = EntityUtils.toString(client.execute(httpPost).getEntity());
// 打印响应内容
System.out.println("Response: " + responseString);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4 个赞
Reno
(Raimbault Bruce)
14
@Hua 6得飞起,帮始皇减轻工作量了,你这属于是
Hua
(Hua)
19
再贴一个python简单的demo
启动后浏览器访问 http://localhost:8181/oauth2/initiate
即可
详情可根据自己的需求来更改
依赖
需要按照Flask和requests
pip install Flask requests
代码
代码
from flask import Flask, session, redirect, request, jsonify
import os
import requests
app = Flask(__name__)
app.secret_key = os.urandom(24)
# OAuth2 参数
CLIENT_ID = 'hi3geJYfTotoiR5S62u3rh4W5tSeC5UG'
CLIENT_SECRET = 'VMPBVoAfOB5ojkGXRDEtzvDhRLENHpaN'
REDIRECT_URI = 'http://localhost:8181/oauth2/callback'
AUTHORIZATION_ENDPOINT = 'https://connect.linux.do/oauth2/authorize'
TOKEN_ENDPOINT = 'https://connect.linux.do/oauth2/token'
USER_ENDPOINT = 'https://connect.linux.do/api/user'
@app.route('/oauth2/initiate')
def initiate_auth():
session['oauth_state'] = os.urandom(16).hex()
authorization_url = f"{AUTHORIZATION_ENDPOINT}?client_id={CLIENT_ID}&response_type=code&redirect_uri={REDIRECT_URI}&state={session['oauth_state']}"
return redirect(authorization_url)
@app.route('/oauth2/callback')
def callback():
code = request.args.get('code')
state = request.args.get('state')
if state != session.get('oauth_state'):
return 'State value does not match', 401
# 请求token
auth = requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)
data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI
}
headers = {'Accept': 'application/json'}
response = requests.post(TOKEN_ENDPOINT, auth=auth, data=data, headers=headers)
if response.status_code == 200:
# 这里获取的rt根据自己的实际进行处理 比如放入会话或数据库里
user_response = requests.get(USER_ENDPOINT, headers={'Authorization': 'Bearer ' + response.json()['access_token']})
if user_response.status_code == 200:
return jsonify(user_response.json())
else:
return 'Failed to fetch user info', user_response.status_code
else:
return 'Failed to fetch access token', response.status_code
if __name__ == '__main__':
app.run(debug=True, port=8181)
注意
代码为demo代码,实际请根据自己所需对token进行处理
2 个赞