{"id":23347,"date":"2024-11-21T16:01:38","date_gmt":"2024-11-21T08:01:38","guid":{"rendered":"https:\/\/fwq.ai\/blog\/23347\/"},"modified":"2024-11-21T16:01:38","modified_gmt":"2024-11-21T08:01:38","slug":"laravel%e5%bc%80%e5%8f%91%ef%bc%9a%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8laravel-sanctum%e5%ae%9e%e7%8e%b0spa%e5%92%8capi%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%ef%bc%9f","status":"publish","type":"post","link":"https:\/\/fwq.ai\/blog\/23347\/","title":{"rendered":"Laravel\u5f00\u53d1\uff1a\u5982\u4f55\u4f7f\u7528Laravel Sanctum\u5b9e\u73b0SPA\u548cAPI\u8eab\u4efd\u9a8c\u8bc1\uff1f"},"content":{"rendered":"<p> sanctum\u662f\u4e00\u4e2a\u8f7b\u91cf\u7ea7\u7684\u8eab\u4efd\u9a8c\u8bc1\u5305\uff0c\u80fd\u591f\u8ba9\u4f60\u5728laravel\u5e94\u7528\u4e2d\u8f7b\u677e\u5730\u5b9e\u73b0api\u8ba4\u8bc1\u548cspa\uff08\u5355\u9875\u5e94\u7528\u7a0b\u5e8f\uff09\u8ba4\u8bc1\u3002\u5728\u672c\u6587\u4e2d\uff0c\u6211\u4eec\u5c06\u63a2\u8ba8\u5982\u4f55\u4f7f\u7528laravel sanctum\u6765\u5b9e\u73b0spa\u548capi\u8eab\u4efd\u9a8c\u8bc1\u3002<\/p>\n<p>\u9996\u5148\uff0c\u8ba9\u6211\u4eec\u770b\u770b\u4ec0\u4e48\u662fSPA\u548cAPI\u8ba4\u8bc1\u3002<\/p>\n<p>SPA\u8ba4\u8bc1\u662f\u6307\u5355\u9875\u5e94\u7528\u7a0b\u5e8f\uff0c\u5b83\u4e0d\u4f1a\u91cd\u65b0\u52a0\u8f7d\u6574\u4e2a\u9875\u9762\uff0c\u800c\u662f\u4f7f\u7528AJAX\u4eceWeb\u670d\u52a1\u5668\u8bf7\u6c42\u4fe1\u606f\uff0c\u4ee5\u6b64\u66f4\u65b0\u5c40\u90e8\u5185\u5bb9\u3002\u5f53\u4f7f\u7528SPA\u65f6\uff0c\u9700\u8981\u5bf9API\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\uff0c\u4ee5\u786e\u4fdd\u53ea\u6709\u7ecf\u8fc7\u8eab\u4efd\u9a8c\u8bc1\u7684\u7528\u6237\u624d\u80fd\u8bbf\u95ee\u5b83\u4eec\u3002<\/p>\n<p>API\u8ba4\u8bc1\u662f\u6307API\u8bf7\u6c42\u8eab\u4efd\u9a8c\u8bc1\u8fc7\u7a0b\u3002\u5f53\u5ba2\u6237\u7aef\u53d1\u9001\u8bf7\u6c42\u65f6\uff0cAPI\u9700\u8981\u9a8c\u8bc1\u8be5\u8bf7\u6c42\u662f\u5426\u6765\u81ea\u6240\u671f\u671b\u7684\u7528\u6237\uff0c\u4ee5\u6b64\u4fdd\u8bc1API\u7aef\u70b9\u4ec5\u7531\u7ecf\u8fc7\u8eab\u4efd\u9a8c\u8bc1\u7684\u7528\u6237\u4f7f\u7528\u3002<\/p>\n<p>\u4e0b\u9762\u662f\u5982\u4f55\u4f7f\u7528Laravel Sanctum\u5b9e\u73b0SPA\u548cAPI\u8eab\u4efd\u9a8c\u8bc1\u7684\u6b65\u9aa4\uff1a<\/p>\n<p>1\u3001\u5b89\u88c5Laravel Sanctum<br \/>\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528composer\u5305\u7ba1\u7406\u5668\u5b89\u88c5Laravel Sanctum\u3002\u5728Laravel\u9879\u76ee\u4e2d\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n<pre>composer require laravel\/sanctum<\/pre>\n<p> \u767b\u5f55\u540e\u590d\u5236 <\/p>\n<p>2\u3001\u8fd0\u884cLaravel Sanctum\u7684\u5b89\u88c5\u5668<br \/>Laravel Sanctum\u63d0\u4f9b\u4e86\u4e00\u4e2a\u5b89\u88c5\u5668\uff0c\u53ef\u4ee5\u5728\u5b89\u88c5\u65f6\u81ea\u52a8\u914d\u7f6e\u6211\u4eec\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u547d\u4ee4\u8fd0\u884c\u6b64\u5b89\u88c5\u5668\uff1a<\/p>\n<pre>php artisan vendor:publish --provider=\"LaravelSanctumSanctumServiceProvider\"<\/pre>\n<p> \u767b\u5f55\u540e\u590d\u5236 <\/p>\n<p>3\u3001\u8fd0\u884c\u8fc1\u79fb<br \/>\u6211\u4eec\u9700\u8981\u8fd0\u884cSanctum\u8fc1\u79fb\u6765\u521b\u5efa\u5fc5\u8981\u7684\u6570\u636e\u5e93\u8868\uff0c\u4ee5\u652f\u6301Sanctum\u7684\u64cd\u4f5c\u3002\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n<pre>php artisan migrate<\/pre>\n<p> \u767b\u5f55\u540e\u590d\u5236 <\/p>\n<p>4\u3001\u914d\u7f6e\u5e94\u7528\u7a0b\u5e8f<br \/>\u6211\u4eec\u9700\u8981\u5c06Laravel Sanctum\u6dfb\u52a0\u5230\u6211\u4eec\u7684\u4e2d\u95f4\u4ef6\u5806\u6808\u4e2d\uff1a<\/p>\n<pre>'api' =&amp;gt; [\n    'middleware' =&amp;gt; ['auth:sanctum'],\n    'throttle:60,1',\n    'prefix' =&amp;gt; 'api',\n    'namespace' =&amp;gt; 'AppHttpControllersAPI',\n],<\/pre>\n<p> \u767b\u5f55\u540e\u590d\u5236 <\/p>\n<p>5\u3001\u4e3a\u7528\u6237\u9881\u53d1\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c<br \/>\u5728Laravel Sanctum\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528tokenCan\u65b9\u6cd5\u68c0\u67e5\u4ee4\u724c\u662f\u5426\u5177\u6709\u7279\u5b9a\u7684API\u6743\u9650\u3002\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528createToken\u65b9\u6cd5\u4e3a\u7528\u6237\u9881\u53d1\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c\uff1a<\/p>\n<pre>use IlluminateHttpRequest;\n\n\/**\n * Store a newly created resource in storage.\n *\n * @param  IlluminateHttpRequest  $request\n * @return IlluminateHttpResponse\n *\/\npublic function store(Request $request)\n{\n    $user = User::find(1);\n    $token = $user-&amp;gt;createToken('token-name', ['server:update'])-&amp;gt;plainTextToken;\n\n    return response()-&amp;gt;json([\n        'access_token' =&amp;gt; $token,\n        'token_type' =&amp;gt; 'Bearer',\n    ]);\n}<\/pre>\n<p> \u767b\u5f55\u540e\u590d\u5236 <\/p>\n<p>\u8fd9\u5c06\u4e3a\u7528\u6237\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a\u2018token-name\u2019\u7684\u4ee4\u724c\uff0c\u8be5\u4ee4\u724c\u5177\u6709server:update\u6743\u9650\u3002<\/p>\n<p>6\u3001\u4fdd\u62a4API\u7aef\u70b9<br \/>\u5728\u6211\u4eec\u7684\u63a7\u5236\u5668\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528\u201cmiddleware\u201d\u65b9\u6cd5\u6765\u4fdd\u62a4API\u7aef\u70b9\uff1a<\/p>\n<pre>public function update(Request $request, $id)\n{\n    if (!$request-&amp;gt;user()-&amp;gt;tokenCan('server:update')) {\n        abort(403, 'Unauthorized');\n    }\n\n    \/\/ Update the server\n}<\/pre>\n<p> \u767b\u5f55\u540e\u590d\u5236 <\/p>\n<p>\u5728\u8fd9\u4e2a\u4f8b\u5b50\u4e2d\uff0c\u6211\u4eec\u53ea\u5141\u8bb8\u90a3\u4e9b\u5177\u6709server:update\u6743\u9650\u7684\u7528\u6237\u8bbf\u95eeupdate\u65b9\u6cd5\u3002<\/p>\n<p>7\u3001\u5728SPA\u4e2d\u4f7f\u7528\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c<br \/>\u5728\u6211\u4eec\u7684SPA\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528Sanctum\u7684@auth\u548c@csrf Blade\u6307\u4ee4\u6765\u83b7\u53d6\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c\uff1a<\/p>\n<pre>\n    &lt;meta charset=\"utf-8\"&gt;&lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;&lt;title&gt;Laravel&lt;\/title&gt;&lt;!-- Fonts --&gt;&lt;link href=\"https:\/\/fonts.googleapis.com\/css?family=Nunito:200,600\" rel=\"stylesheet\"&gt;&lt;script src=\"%7B%7B%20asset('js\/app.js')%20%7D%7D\" defer&gt;&lt;\/script&gt;&lt;!-- Styles --&gt;&lt;link href=\"%7B%7B%20asset('css\/app.css')%20%7D%7D\" rel=\"stylesheet\"&gt;&lt;!-- Meta --&gt;&lt;meta name=\"csrf-token\" content=\"{{ csrf_token() }}\"&gt;&lt;div id=\"app\"&gt;\n        &lt;nav class=\"navbar navbar-expand-md navbar-light bg-white shadow-sm\"&gt;&lt;div class=\"container-fluid\"&gt;\n                &lt;a class=\"navbar-brand\" href=\"%7B%7B%20url('\/')%20%7D%7D\"&gt;\n                    {{ config('app.name', 'Laravel') }}\n                &lt;\/a&gt;\n                &lt;button class=\"navbar-toggler\" type=\"button\" data-toggle=\"collapse\" data-target=\"#navbarSupportedContent\" aria-controls=\"navbarSupportedContent\" aria-expanded=\"false\" aria-label=\"{{ __('Toggle navigation') }}\"&gt;\n                    &lt;span class=\"navbar-toggler-icon\"&gt;&lt;\/span&gt;\n                &lt;\/button&gt;\n\n                &lt;div class=\"collapse navbar-collapse\" id=\"navbarSupportedContent\"&gt;\n                    &lt;!-- Left Side Of Navbar --&gt;\n                    &lt;ul class=\"navbar-nav mr-auto\"&gt;&lt;\/ul&gt;\n&lt;!-- Right Side Of Navbar --&gt;&lt;ul class=\"navbar-nav ml-auto\"&gt;\n&lt;!-- Authentication Links --&gt;\n                        @guest\n                            &lt;li class=\"nav-item\"&gt;\n                                &lt;a class=\"nav-link\" href=\"%7B%7B%20route('login')%20%7D%7D\"&gt;{{ __('Login') }}&lt;\/a&gt;\n                            &lt;\/li&gt;\n                            @if (Route::has('register'))\n                                &lt;li class=\"nav-item\"&gt;\n                                    &lt;a class=\"nav-link\" href=\"%7B%7B%20route('register')%20%7D%7D\"&gt;{{ __('Register') }}&lt;\/a&gt;\n                                &lt;\/li&gt;\n                            @endif\n                        @else\n                            &lt;li class=\"nav-item dropdown\"&gt;\n                                &lt;a id=\"navbarDropdown\" class=\"nav-link dropdown-toggle\" href=\"#\" role=\"button\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\" v-pre&gt;\n                                    {{ Auth::user()-&amp;gt;name }}\n                                &lt;\/a&gt;\n\n                                &lt;div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"navbarDropdown\"&gt;\n                                    &lt;a class=\"dropdown-item\" href=\"%7B%7B%20route('logout')%20%7D%7D\" onclick=\"event.preventDefault();\n                                                     document.getElementById('logout-form').submit();\"&gt;\n                                        {{ __('Logout') }}\n                                    &lt;\/a&gt;\n\n                                    &lt;form id=\"logout-form\" action=\"%7B%7B%20route('logout')%20%7D%7D\" method=\"POST\" style=\"display: none;\"&gt;\n                                        @csrf\n                                    &lt;\/form&gt;\n                                &lt;\/div&gt;\n                            &lt;\/li&gt;\n                        @endguest\n                    &lt;\/ul&gt;\n&lt;\/div&gt;\n            &lt;\/div&gt;\n        &lt;\/nav&gt;&lt;main class=\"py-4\"&gt;\n            @yield('content')\n        &lt;\/main&gt;\n&lt;\/div&gt;\n    @auth\n        &lt;script&gt;\n            window.Laravel = {!! json_encode([\n                'csrf_token' =&gt; csrf_token(),\n                'api_token' =&gt; Auth::user()-&gt;api_token\n            ]) !!};\n        &lt;\/script&gt;\n    @endauth\n<\/pre>\n<p> \u767b\u5f55\u540e\u590d\u5236 <\/p>\n<p>\u5728\u8fd9\u4e2a\u4f8b\u5b50\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528\u4e86Sanctum\u7684@auth\u548c@csrf Blade\u6307\u4ee4\uff0c\u7528\u4e8e\u83b7\u53d6\u7528\u6237\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c\u548cCSRF\u4ee4\u724c\u3002<\/p>\n<p>\u8fd9\u5c31\u662f\u5982\u4f55\u4f7f\u7528Laravel Sanctum\u5b9e\u73b0SPA\u548cAPI\u8eab\u4efd\u9a8c\u8bc1\u7684\u5168\u90e8\u5185\u5bb9\u3002\u4f7f\u7528Laravel Sanctum\u53ef\u4ee5\u8f7b\u677e\u5730\u4fdd\u62a4\u6211\u4eec\u7684API\u7aef\u70b9\u548cSPA\u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u5e2e\u52a9\u6211\u4eec\u5b9e\u73b0\u6700\u4f73\u5b89\u5168\u5b9e\u8df5\u3002<\/p>\n<p>\u4ee5\u4e0a\u5c31\u662fLaravel\u5f00\u53d1\uff1a\u5982\u4f55\u4f7f\u7528Laravel Sanctum\u5b9e\u73b0SPA\u548cAPI\u8eab\u4efd\u9a8c\u8bc1\uff1f\u7684\u8be6\u7ec6\u5185\u5bb9\uff0c\u66f4\u591a\u8bf7\u5173\u6ce8\u7c73\u4e91\u5176\u5b83\u76f8\u5173\u6587\u7ae0\uff01<\/p>\n","protected":false},"excerpt":{"rendered":"<p>sanctum\u662f\u4e00\u4e2a\u8f7b\u91cf\u7ea7\u7684\u8eab\u4efd\u9a8c\u8bc1\u5305\uff0c\u80fd\u591f\u8ba9\u4f60\u5728laravel\u5e94\u7528\u4e2d\u8f7b\u677e\u5730\u5b9e\u73b0api\u8ba4\u8bc1\u548cspa\uff08\u5355\u9875\u5e94\u7528\u7a0b\u5e8f\uff09\u8ba4\u8bc1\u3002\u5728\u672c\u6587\u4e2d\uff0c\u6211\u4eec\u5c06\u63a2\u8ba8\u5982\u4f55\u4f7f\u7528laravel sanctum\u6765\u5b9e\u73b0spa\u548capi\u8eab\u4efd\u9a8c\u8bc1\u3002 \u9996\u5148\uff0c\u8ba9\u6211\u4eec\u770b\u770b\u4ec0\u4e48\u662fSPA\u548cAPI\u8ba4\u8bc1\u3002 SPA\u8ba4\u8bc1\u662f\u6307\u5355\u9875\u5e94\u7528\u7a0b\u5e8f\uff0c\u5b83\u4e0d\u4f1a\u91cd\u65b0\u52a0\u8f7d\u6574\u4e2a\u9875\u9762\uff0c\u800c\u662f\u4f7f\u7528AJAX\u4eceWeb\u670d\u52a1\u5668\u8bf7\u6c42\u4fe1\u606f\uff0c\u4ee5\u6b64\u66f4\u65b0\u5c40\u90e8\u5185\u5bb9\u3002\u5f53\u4f7f\u7528SPA\u65f6\uff0c\u9700\u8981\u5bf9API\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\uff0c\u4ee5\u786e\u4fdd\u53ea\u6709\u7ecf\u8fc7\u8eab\u4efd\u9a8c\u8bc1\u7684\u7528\u6237\u624d\u80fd\u8bbf\u95ee\u5b83\u4eec\u3002 API\u8ba4\u8bc1\u662f\u6307API\u8bf7\u6c42\u8eab\u4efd\u9a8c\u8bc1\u8fc7\u7a0b\u3002\u5f53\u5ba2\u6237\u7aef\u53d1\u9001\u8bf7\u6c42\u65f6\uff0cAPI\u9700\u8981\u9a8c\u8bc1\u8be5\u8bf7\u6c42\u662f\u5426\u6765\u81ea\u6240\u671f\u671b\u7684\u7528\u6237\uff0c\u4ee5\u6b64\u4fdd\u8bc1API\u7aef\u70b9\u4ec5\u7531\u7ecf\u8fc7\u8eab\u4efd\u9a8c\u8bc1\u7684\u7528\u6237\u4f7f\u7528\u3002 \u4e0b\u9762\u662f\u5982\u4f55\u4f7f\u7528Laravel Sanctum\u5b9e\u73b0SPA\u548cAPI\u8eab\u4efd\u9a8c\u8bc1\u7684\u6b65\u9aa4\uff1a 1\u3001\u5b89\u88c5Laravel Sanctum\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528composer\u5305\u7ba1\u7406\u5668\u5b89\u88c5Laravel Sanctum\u3002\u5728Laravel\u9879\u76ee\u4e2d\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a composer require laravel\/sanctum \u767b\u5f55\u540e\u590d\u5236 2\u3001\u8fd0\u884cLaravel Sanctum\u7684\u5b89\u88c5\u5668Laravel Sanctum\u63d0\u4f9b\u4e86\u4e00\u4e2a\u5b89\u88c5\u5668\uff0c\u53ef\u4ee5\u5728\u5b89\u88c5\u65f6\u81ea\u52a8\u914d\u7f6e\u6211\u4eec\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u547d\u4ee4\u8fd0\u884c\u6b64\u5b89\u88c5\u5668\uff1a php artisan vendor:publish &#8211;provider=&#8221;LaravelSanctumSanctumServiceProvider&#8221; \u767b\u5f55\u540e\u590d\u5236 3\u3001\u8fd0\u884c\u8fc1\u79fb\u6211\u4eec\u9700\u8981\u8fd0\u884cSanctum\u8fc1\u79fb\u6765\u521b\u5efa\u5fc5\u8981\u7684\u6570\u636e\u5e93\u8868\uff0c\u4ee5\u652f\u6301Sanctum\u7684\u64cd\u4f5c\u3002\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a php artisan migrate \u767b\u5f55\u540e\u590d\u5236 4\u3001\u914d\u7f6e\u5e94\u7528\u7a0b\u5e8f\u6211\u4eec\u9700\u8981\u5c06Laravel Sanctum\u6dfb\u52a0\u5230\u6211\u4eec\u7684\u4e2d\u95f4\u4ef6\u5806\u6808\u4e2d\uff1a &#8216;api&#8217; =&amp;gt; [ &#8216;middleware&#8217; =&amp;gt; [&#8216;auth:sanctum&#8217;], &#8216;throttle:60,1&#8217;, &#8216;prefix&#8217; =&amp;gt; &#8216;api&#8217;, &#8216;namespace&#8217; =&amp;gt; &#8216;AppHttpControllersAPI&#8217;, ], \u767b\u5f55\u540e\u590d\u5236 5\u3001\u4e3a\u7528\u6237\u9881\u53d1\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c\u5728Laravel Sanctum\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528tokenCan\u65b9\u6cd5\u68c0\u67e5\u4ee4\u724c\u662f\u5426\u5177\u6709\u7279\u5b9a\u7684API\u6743\u9650\u3002\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528createToken\u65b9\u6cd5\u4e3a\u7528\u6237\u9881\u53d1\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c\uff1a use IlluminateHttpRequest; \/** * Store a newly created resource [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16],"tags":[],"class_list":["post-23347","post","type-post","status-publish","format-standard","hentry","category-16"],"_links":{"self":[{"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/posts\/23347","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/comments?post=23347"}],"version-history":[{"count":0,"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/posts\/23347\/revisions"}],"wp:attachment":[{"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/media?parent=23347"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/categories?post=23347"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fwq.ai\/blog\/wp-json\/wp\/v2\/tags?post=23347"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}