<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Dev on Intersteller Cabin</title><link>https://www.starsac.cn/tags/dev/</link><description>Recent content in Dev on Intersteller Cabin</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Fri, 03 Apr 2026 22:25:35 +0800</lastBuildDate><atom:link href="https://www.starsac.cn/tags/dev/index.xml" rel="self" type="application/rss+xml"/><item><title>uv笔记</title><link>https://www.starsac.cn/posts/uv-note/</link><pubDate>Fri, 03 Apr 2026 22:25:35 +0800</pubDate><guid>https://www.starsac.cn/posts/uv-note/</guid><description>&lt;blockquote&gt;
&lt;p&gt;其它信息参阅&lt;a href="https://uv.doczh.com/" class="external-link" target="_blank" rel="noopener"&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="安装"&gt;
 安装
 &lt;a class="heading-link" href="#%e5%ae%89%e8%a3%85"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="macos-和-linux"&gt;
 macOS 和 Linux
 &lt;a class="heading-link" href="#macos-%e5%92%8c-linux"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -LsSf https://astral.sh/uv/install.sh | sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者使用Homebrew&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install uv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="windows"&gt;
 Windows
 &lt;a class="heading-link" href="#windows"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;powershell -ExecutionPolicy ByPass -c &lt;span style="color:#a5d6ff"&gt;&amp;#34;irm https://astral.sh/uv/install.ps1 | iex&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者使用winget&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;winget install --id&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;astral-sh.uv -e
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="升级"&gt;
 升级
 &lt;a class="heading-link" href="#%e5%8d%87%e7%ba%a7"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uv self update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="python-版本管理"&gt;
 Python 版本管理
 &lt;a class="heading-link" href="#python-%e7%89%88%e6%9c%ac%e7%ae%a1%e7%90%86"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;uv python install&lt;/code&gt;: 安装 Python 版本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv python list&lt;/code&gt;: 查看可用 Python 版本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv python find&lt;/code&gt;: 查找已安装的 Python 版本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv python pin&lt;/code&gt;: 将当前项目固定使用特定 Python 版本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv python uninstall&lt;/code&gt;: 卸载 Python 版本&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="脚本运行"&gt;
 脚本运行
 &lt;a class="heading-link" href="#%e8%84%9a%e6%9c%ac%e8%bf%90%e8%a1%8c"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;执行独立的 Python 脚本，例如 &lt;code&gt;example.py&lt;/code&gt;。&lt;/p&gt;</description></item><item><title>FastAPI - OAuth2+JWT认证</title><link>https://www.starsac.cn/posts/fastapi-oauth-jwt-authorization/</link><pubDate>Sat, 05 Apr 2025 15:24:35 +0800</pubDate><guid>https://www.starsac.cn/posts/fastapi-oauth-jwt-authorization/</guid><description>&lt;h1 id="fastapi---oauth2jwt认证"&gt;
 FastAPI - OAuth2+JWT认证
 &lt;a class="heading-link" href="#fastapi---oauth2jwt%e8%ae%a4%e8%af%81"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;OAuth2只是一种登录验证的模式，JWT只是登陆后生成，用来简化登录操作的token，是Bearer令牌的一种实现&lt;/p&gt;
&lt;h2 id="验证token"&gt;
 验证token
 &lt;a class="heading-link" href="#%e9%aa%8c%e8%af%81token"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;引入OAuth2PasswordBearer，创建该类的示例，这个实例是一个可调用对象，会自动去Header里面找token并返回，如果找不到则抛出401Unauthorize异常&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;from&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;fastapi.security&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;import&lt;/span&gt; OAuth2PasswordBearer
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;oauth2_scheme &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; OAuth2PasswordBearer(tokenUrl&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;token&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;在需要认证token的路径处理函数里面声明收到token，引入oauth2_scheme依赖项，如果有token则会执行函数体&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;/token-string&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;async&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;def&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;get_token_string&lt;/span&gt;(token: Annotated[str, Depends(oauth2_scheme)]):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; {&lt;span style="color:#a5d6ff"&gt;&amp;#34;token&amp;#34;&lt;/span&gt;: token}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时只要我们请求头中有token（无论值是什么），就可以访问这个路由了&lt;/p&gt;
&lt;p&gt;&lt;img src="https://resources.starsac.cn/2025/04/image-20250405183414873.png" alt="image-20250405183414873"&gt;&lt;/p&gt;
&lt;h2 id="生成token"&gt;
 生成token
 &lt;a class="heading-link" href="#%e7%94%9f%e6%88%90token"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;在创建oauth2_scheme时我们声明了获得token的路径，现在我们编写生成token的具体实现。&lt;/p&gt;
&lt;p&gt;OAuth2要求username和password两个必选字段必须通过表单数据的形式发送，也就是设置&lt;code&gt;Content-Type:application/x-www-form-urlencoded&lt;/code&gt;,将数据放在请求体里面&lt;/p&gt;
&lt;p&gt;以&lt;code&gt;grant_type=password&amp;amp;username=john&amp;amp;password=b&lt;/code&gt;的形式发送给后端。&lt;/p&gt;
&lt;p&gt;为了接收发送过来的参数，可以使用&lt;code&gt;OAuth2PasswordRequestForm&lt;/code&gt;，这是一个类依赖项，可以省略Depends()里面的内容&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;from&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;fastapi.security&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;import&lt;/span&gt; OAuth2PasswordRequestForm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;/token&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;def&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;post_token&lt;/span&gt;(form_data:Annotated[OAuth2PasswordRequestForm,Depends()]):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;# 这里可以判断用户登录是否合法，返回响应结果或者抛出异常&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; form_data&lt;span style="color:#ff7b72;font-weight:bold"&gt;.&lt;/span&gt;username
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; password &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; form_data&lt;span style="color:#ff7b72;font-weight:bold"&gt;.&lt;/span&gt;password
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; user_dict&lt;span style="color:#ff7b72;font-weight:bold"&gt;.&lt;/span&gt;get(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(user, name, password)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;not&lt;/span&gt; user:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;raise&lt;/span&gt; HTTPException(status_code&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;400&lt;/span&gt;, detail&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;invalidUser&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;not&lt;/span&gt; password &lt;span style="color:#ff7b72;font-weight:bold"&gt;==&lt;/span&gt; user&lt;span style="color:#ff7b72;font-weight:bold"&gt;.&lt;/span&gt;get(&lt;span style="color:#a5d6ff"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;raise&lt;/span&gt; HTTPException(status_code&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;400&lt;/span&gt;, detail&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;invalidPasswd&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; {&lt;span style="color:#a5d6ff"&gt;&amp;#34;access_token&amp;#34;&lt;/span&gt;: name, &lt;span style="color:#a5d6ff"&gt;&amp;#34;token_type&amp;#34;&lt;/span&gt;: &lt;span style="color:#a5d6ff"&gt;&amp;#34;bearer&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该路径函数的返回值格式是固定的，是OAuth2规范的一部分。&lt;/p&gt;</description></item></channel></rss>