Laravel Vuejs 实战:开发知乎 (21)前后端分离 API token 认证

解决上一节当中如果api路由改成:

  1 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');

之后 axios ajax post请求报 401 unauthorized Vampire bat异常的问题。

原理Open-mouthed smile

Laravel Passport

Passport OAuth 认证

教程School

Building SPAs with Laravel 5 and Vue.js 2 - Finishing Up

API 认证解决方案:Laravel Passport

API Authentication in Laravel-Vue SPA using Jwt-auth

再增加一个:OAuth2 认证机制 Token 原理 以及 Laravel 安装 Passport 身份验证

修改指导:简单来说,auth要求的token我们没有提供Crying face

批注 2020-03-01 195846

通过api访问走的是token认证,这里没有提供token所以就认证失败返回401了

这个tokendriver对应的其实就是:

批注 2020-03-01 200051

这个TokenGuard.php文件,这里面需要一个api_token。需要在request里提供api_token参数,为了区别是哪个用户,需要在user表添加api_token字段。认证过程调用的是TokenGuard.php中的getTokenForRequest方法:

批注 2020-03-01 202202

这个bearerToken实际找header中是否存在Authorization

批注 2020-03-01 202301

我们需要来提供这个token:

原理参考:

BearerToken:

本质上给用户表添加api_token,后台根据这个字段判断是否是有效的用户,无效返回401,有效返回查询结果。
优点是容易理解,缺点太简单,安全也不够。
为了安全,可以实现下面的功能:

每次登录成功后刷新api_token为新值
其实 Laravel 官方提供了一个
Laravel Passport 的包。Laravel Passport is an OAuth2 server and API authentication package 。

Laravel Vue 前后端分离 使用token认证 

搞一搞laravel里api路由的 auth:api 和 api_token

Laravel API Token 体验

JWT(Json Web Token):

Laravel 5 中使用 JWT(Json Web Token) 实现基于API的用户认证

【但是Thumbs down并不建议真实生产环境下用下面的方法,建议用官方的passport或者jwtPointing up

步骤:

执行命令:

  1 php artisan make:migration add_api_token_to_users_table --table=users

编辑****_add_api_token_to_users_table.php文件:

  1 <?php
  2 
  3 use Illuminate\Database\Migrations\Migration;
  4 use Illuminate\Database\Schema\Blueprint;
  5 use Illuminate\Support\Facades\Schema;
  6 
  7 class AddApiTokenToUsersTable extends Migration
  8 {
  9     /**
 10      * Run the migrations.
 11      *
 12      * @return void
 13      */
 14     public function up()
 15     {
 16         Schema::table('users', function (Blueprint $table) {
 17             //
 18             $table->string('api_token', 64)->unique()->comment("api验证token");
 19         });
 20     }
 21 
 22     /**
 23      * Reverse the migrations.
 24      *
 25      * @return void
 26      */
 27     public function down()
 28     {
 29         Schema::table('users', function (Blueprint $table) {
 30             //
 31             $table->dropColumn('api_token');
 32         });
 33     }
 34 }
 35 
 36 
_add_api_token_to_users_table.php

执行命令:

  1 php artisan migrate

其余的参考Laravel Vue 前后端分离 使用token认证 按照此链接教程,完成后打开网页刷新,再点击关注按钮,axios提交的时候不会报401异常了,记得修改 api.php中:

  1 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');


修改一下之前的两个错误:

1.因为之前采用的是

一个路由 处理刷新取关注状态和按下按钮关注的逻辑,这样一刷新页面就又相当于执行了一次关注/取关操作,必须分割开

  1 //加载页面时取关注状态
  2 Route::middleware('auth:api')->post('/questions/follow/stats', 'QuestionController@getFollowStats');
  3 //执行关注/取关操作
  4 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');
  5 

然后,对应需要在QuestionController添加一个方法取关注状态,

2.之前判断用户可否关注的逻辑错误

  1 public function getFollowStats(Request $request)
  2 {
  3     $user = auth()->guard('api')->user();
  4     $question = Question::find($request->get('question'));
  5 
  6     $followable = $user->can('follow', $question);
  7 
  8     return response()->json([
  9         'followable' => $followable,
 10     ]);
 11 }
 12 
 13 public function followThroughApi(Request $request)
 14 {
 15     $user = auth()->guard('api')->user();
 16     $question = Question::find($request->get('question'));
 17 
 18     //同步记录
 19     $user->followQuestions()->toggle($question->id);
 20     $question->followers_count = $question->followUsers()->count();
 21     $question->update();
 22     //判断用户关注状态
 23     $followable = $user->can('follow', $question);
 24 
 25     return response()->json([
 26         'followable' => $followable,
 27     ]);
 28 }
 29 

再然后,优化一下,用户的数据不由props传递,也不由axios提交,因为有bearer token了。


接着QuestionController中followThroughApi和getFollowStats方法内,

$user获取: 直接通过 auth()->user()auth()->guard('api')->user(); 获取即可。


QuestionController.php

  1 <?php
  2 
  3 namespace App\Http\Controllers;
  4 
  5 use App\Http\Requests\QuestionStoreRequest;
  6 use App\Models\Question;
  7 use App\Repositories\QuestionRepository;
  8 use App\User;
  9 use Illuminate\Http\Request;
 10 
 11 class QuestionController extends Controller
 12 {
 13 
 14     /**
 15      * @var QuestionRepository
 16      */
 17     private $questionRepository;
 18 
 19     public function __construct(QuestionRepository $questionRepository)
 20     {
 21         $this->middleware(
 22             'auth',
 23             [
 24                 'except' =>
 25                     [
 26                         'index',
 27                         'show',
 28                         'followThroughApi'
 29                     ]//非注册用户只能查看不能编辑添加更改删除
 30             ]
 31         );
 32 
 33         $this->questionRepository = $questionRepository;
 34     }
 35 
 36 
 37     /** Display a listing of the resource.
 38      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 39      */
 40     public function index()
 41     {
 42         //
 43         $questions = $this->questionRepository->getQuestionPublished();
 44         return view('questions.index', compact('questions'));
 45     }
 46 
 47 
 48     /**
 49      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 50      */
 51     public function create()
 52     {
 53         //
 54         return view('questions.create');
 55     }
 56 
 57 
 58     /**
 59      * @param QuestionStoreRequest $request
 60      * @return \Illuminate\Http\RedirectResponse
 61      */
 62     public function store(QuestionStoreRequest $request)//依赖注入QuestionStoreRequest实例
 63     {
 64         //
 65 //        $data = $request->validate([
 66 //            'title' => 'required|min:8',
 67 //            'content' => 'required|min:28',
 68 //        ]);
 69         //存储topics
 70         $topics = $this->questionRepository->normalizeTopics($request->get('topics'));
 71         //初始化question要用到的数据
 72         $data = $request->all();
 73         $data['user_id'] = auth()->user()->id;
 74 
 75 //        $question=Question::create($data); 被下方代码取代
 76         $question = $this->questionRepository->create($data);
 77 
 78         //使用我们再question model里面添加的topics方法获得 topics关联,再使用attach方法
 79         $question->topics()->attach($topics);
 80 
 81         return redirect()->route('questions.show', $question);
 82     }
 83 
 84 
 85     /**
 86      * @param Question $question
 87      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 88      */
 89     public function show(Question $question)
 90     {
 91         //使用关系关联加载,with方法会将分类之下的主题一起查询出来,而且不会出现N+1影响性能的问题
 92         $question->with('topics')->get();
 93         //使用关系关联加载,with方法会将分类之下的回答一起查询出来,而且不会出现N+1影响性能的问题
 94         $question->with('answers')->get();
 95 
 96         return view('questions.show', compact('question'));
 97     }
 98 
 99 
100     /**判断权限 返回视图
101      * @param Question $question
102      * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
103      */
104     public function edit(Question $question)
105     {
106         if (auth()->user()->can('update', $question)) //判断当前用户是否有权编辑更新该question实例
107         {
108             //返回编辑视图
109             return view('questions.edit', compact('question'));
110         } else {
111             //返回警告 没有权限
112             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
113         }
114     }
115 
116 
117     /** Update the specified resource in storage.
118      * @param QuestionStoreRequest $questionStoreRequest
119      * @param Question $question
120      * @return \Illuminate\Http\RedirectResponse
121      */
122     public function update(QuestionStoreRequest $questionStoreRequest, Question $question)
123     {
124         //更新前 判断下权限
125         if (!(auth()->user()->can('update', $question))) {
126             //返回警告 没有权限
127             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
128         }
129         //取得更新的字段 使用Eloquent提供的update方法执行问题更新
130         $question->update([
131             'title' => $questionStoreRequest->get('title'),
132             'content' => $questionStoreRequest->get('content'),
133         ]);
134 
135 
136         //topics的操作这时候看起来有点臃肿 可以使用TopicController来管理,暂时省略
137         //存储topics
138         $topics = $this->questionRepository->normalizeTopics($questionStoreRequest->get('topics'));
139         //使用我们再question model里面添加的topics方法获得 topics关联,
140         //再使用sync方法同步tag 【删除的会被删除掉,没删除的就保留,新的就增加】
141         $question->topics()->sync($topics);
142 
143         //更新完成,跳转回去
144         return redirect()->back();
145     }
146 
147 
148     /**Remove the specified resource from storage.
149      * @param Question $question
150      * @return \Illuminate\Http\RedirectResponse
151      * @throws \Exception
152      */
153     public function destroy(Question $question)
154     {
155         //
156         if (auth()->user()->can('destroy', $question)) {
157             $question->delete();
158             return redirect()->route('questions.index')->with('success', "删除成功!");
159         }
160         return redirect()->back()->with('danger', "你不能删除不属于你的问题!");
161     }
162 
163 
164     public function follow(Question $question)
165     {
166         if (auth()->user()->can('follow', $question)) //通过QuestionPolicy的follow方法判断用户是否可以关注问题
167         {
168             $message = "关注";
169         } else {
170             $message = "取关";
171         }
172         //同步记录
173         auth()->user()->followQuestions()->toggle($question);
174         $question->followers_count = $question->followUsers()->count();
175         $question->save();
176         return redirect()->back()->with('success', $message . '成功!');
177     }
178 
179     public function getFollowStats(Request $request)
180     {
181         $user = auth()->guard('api')->user();
182         $question = Question::find($request->get('question'));
183 
184         $followable = $user->can('follow', $question);
185 
186         return response()->json([
187             'followable' => $followable,
188         ]);
189     }
190 
191     public function followThroughApi(Request $request)
192     {
193         $user = auth()->guard('api')->user();
194         $question = Question::find($request->get('question'));
195 
196         //同步记录
197         $user->followQuestions()->toggle($question->id);
198         $question->followers_count = $question->followUsers()->count();
199         $question->update();
200         //判断用户关注状态
201         $followable = $user->can('follow', $question);
202 
203         return response()->json([
204             'followable' => $followable,
205         ]);
206     }
207 }
208 
209 
QuestionController.php

QuestionPolicy.php

  1 <?php
  2 
  3 namespace App\Policies;
  4 
  5 use App\Models\Question;
  6 use App\User;
  7 use Illuminate\Auth\Access\HandlesAuthorization;
  8 
  9 class QuestionPolicy
 10 {
 11     use HandlesAuthorization;
 12 
 13     /**
 14      * Create a new policy instance.
 15      *
 16      * @return void
 17      */
 18     public function __construct()
 19     {
 20         //
 21 
 22     }
 23 
 24 
 25     /**
 26      * 判断用户是否有权编辑更新问题
 27      * @param User $user
 28      * @param Question $question
 29      * @return bool
 30      */
 31     public function update(User $user, Question $question)
 32     {
 33         return $user->id === $question->user_id;
 34     }
 35 
 36 
 37     /**
 38      * 判断用户是否有权删除问题
 39      * @param User $user
 40      * @param Question $question
 41      * @return bool
 42      */
 43     public function destroy(User $user, Question $question)
 44     {
 45         return $user->id === $question->user_id;
 46     }
 47 
 48 
 49     /** 用户是否可以关注问题,未登录不行,关注了不行
 50      * @param User $user
 51      * @param Question $question
 52      * @return bool
 53      */
 54     public function follow(User $user, Question $question)
 55     {
 56         //axiox api 需要auth:api 先不实现,注释掉
 57         if (auth()->check()) {
 58             return !($user->followQuestions->contains('id', $question->id));
 59         } else {
 60 
 61         }
 62     }
 63 }
 64 
 65 
QuestionPolicy.php

app.blade.php

  1 <!doctype html>
  2 <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  3 <head>
  4     <meta charset="utf-8">
  5     <meta name="viewport" content="width=device-width, initial-scale=1">
  6 
  7     {{--    CSRF Token--}}
  8     <meta name="csrf-token" content="{{ csrf_token() }}">
  9     {{--    api bearer Token--}}
 10     <meta name="api-token" content="{{ Auth::check() ? 'Bearer '.auth()->user()->api_token : 'Bearer ' }}">
 11 
 12     <title>{{ config('app.name', 'Laravel') }}</title>
 13 
 14 
 15     {{--    Fonts--}}
 16     <link rel="dns-prefetch" href="//fonts.gstatic.com">
 17     <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
 18 
 19     {{--    Styles--}}
 20     <link href="{{ mix('css/app.css') }}" rel="stylesheet">
 21 
 22 </head>
 23 <body>
 24 <div id="app">
 25     <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
 26         <div class="container">
 27             <a class="navbar-brand" href="{{ url('/') }}">
 28                 {{ config('app.name', 'Laravel') }}
 29             </a>
 30             <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
 31                     aria-controls="navbarSupportedContent" aria-expanded="false"
 32                     aria-label="{{ __('Toggle navigation') }}">
 33                 <span class="navbar-toggler-icon"></span>
 34             </button>
 35 
 36             <div class="collapse navbar-collapse" id="navbarSupportedContent">
 37                 {{--                Left Side Of Navbar--}}
 38                 <ul class="navbar-nav mr-auto">
 39 
 40                 </ul>
 41 
 42                 {{--                Right Side Of Navbar--}}
 43                 <ul class="navbar-nav ml-auto">
 44                     {{--                    Authentication Links--}}
 45                     @guest
 46                         <li class="nav-item">
 47                             <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
 48                         </li>
 49                         @if (Route::has('register'))
 50                             <li class="nav-item">
 51                                 <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
 52                             </li>
 53                         @endif
 54                     @else
 55                         <li class="nav-item dropdown">
 56                             <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button"
 57                                data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
 58                                 {{ Auth::user()->name }} <span class="caret"></span>
 59                             </a>
 60 
 61                             <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
 62                                 <a class="dropdown-item" href="{{ route('logout') }}"
 63                                    onclick="event.preventDefault();
 64                                                      document.getElementById('logout-form').submit();">
 65                                     {{ __('Logout') }}
 66                                 </a>
 67 
 68                                 <form id="logout-form" action="{{ route('logout') }}" method="POST"
 69                                       style="display: none;">
 70                                     @csrf
 71                                 </form>
 72                             </div>
 73                         </li>
 74                     @endguest
 75                 </ul>
 76             </div>
 77         </div>
 78     </nav>
 79 
 80     <main class="py-4">
 81         @include('flash::message')
 82         @yield('content')
 83     </main>
 84 </div>
 85 {{--Scripts--}}
 86 <script src="{{ mix('js/app.js') }}"></script>
 87 
 88 <script>
 89     $('#flash-overlay-modal').modal();
 90     window.UEDITOR_CONFIG.serverUrl = "{{ config('ueditor.route.name') }}";
 91 </script>
 92 @yield('footer-js')
 93 </body>
 94 </html>
 95 
 96 
app.blade.php

api.php

  1 <?php
  2 
  3 use Illuminate\Http\Request;
  4 
  5 /*
  6 |--------------------------------------------------------------------------
  7 | API Routes
  8 |--------------------------------------------------------------------------
  9 |
 10 | Here is where you can register API routes for your application. These
 11 | routes are loaded by the RouteServiceProvider within a group which
 12 | is assigned the "api" middleware group. Enjoy building your API!
 13 |
 14 */
 15 
 16 Route::middleware('auth:api')->get('/user', function (Request $request) {
 17     return $request->user();
 18 });
 19 
 20 Route::middleware('api')->get('/topics', function (Request $request) {
 21     $query = $request->query('q');
 22     return \App\Topic::query()->where('name', 'like', '%' . $query . '%')->get();
 23 });
 24 //加载页面时取关注状态
 25 Route::middleware('auth:api')->post('/questions/follow/stats', 'QuestionController@getFollowStats');
 26 //执行关注/取关操作
 27 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi');
 28 
 29 
 30 
 31 
api.php

bootstrap.js

  1 window._ = require('lodash');
  2 
  3 /**
  4  * We'll load jQuery and the Bootstrap jQuery plugin which provides support
  5  * for JavaScript based Bootstrap features such as modals and tabs. This
  6  * code may be modified to fit the specific needs of your application.
  7  */
  8 
  9 try {
 10     window.Popper = require('popper.js').default;
 11     window.$ = window.jQuery = require('jquery');
 12 
 13     require('bootstrap');
 14 } catch (e) {
 15 }
 16 
 17 /**
 18  * We'll load the axios HTTP library which allows us to easily issue requests
 19  * to our Laravel back-end. This library automatically handles sending the
 20  * CSRF token as a header based on the value of the "XSRF" token cookie.
 21  */
 22 
 23 window.axios = require('axios');
 24 
 25 window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
 26 
 27 let api_token = document.head.querySelector('meta[name="api-token"]');
 28 
 29 if (api_token) {
 30     window.axios.defaults.headers.common['Authorization'] = api_token.content;
 31 } else {
 32     console.error('Authorization token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
 33 }
 34 
 35 /**
 36  * Echo exposes an expressive API for subscribing to channels and listening
 37  * for events that are broadcast by Laravel. Echo and event broadcasting
 38  * allows your team to easily build robust real-time web applications.
 39  */
 40 
 41 // import Echo from 'laravel-echo';
 42 
 43 // window.Pusher = require('pusher-js');
 44 
 45 // window.Echo = new Echo({
 46 //     broadcaster: 'pusher',
 47 //     key: process.env.MIX_PUSHER_APP_KEY,
 48 //     cluster: process.env.MIX_PUSHER_APP_CLUSTER,
 49 //     encrypted: true
 50 // });
 51 
 52 
bootstrap.js

QuestionFollowButton.vue

  1 <template>
  2     <button :class="classObject"
  3             @click="follow"
  4             v-text="text">
  5     </button>
  6 </template>
  7 
  8 <script>
  9     export default {
 10         props: ['question'],
 11         name: "QuestionFollowButton",
 12         data() {
 13             return {
 14                 followable: true,
 15             }
 16         },
 17         computed: {
 18             text() {
 19                 return this.followable ? "关注用户" : "取消关注";
 20             },
 21             classObject() {
 22                 return this.followable ? "btn btn-block btn-primary" : "btn btn-block btn-danger";
 23             },
 24         },
 25         mounted: function () {
 26             let currentObj = this;
 27             axios.post('/api/questions/follow/stats', {'question': this.question})
 28                 .then(function (response) {
 29                     currentObj.followable = response.data.followable;
 30                 })
 31                 .catch(function (e) {
 32                     console.log(e);
 33                 });
 34         },
 35         methods: {
 36             follow() {
 37                 let currentObj = this;
 38                 axios.post('/api/questions/follow', {'question': this.question})
 39                     .then(function (response) {
 40                             currentObj.followable = response.data.followable;
 41                         }
 42                     )
 43                     .catch(function (e) {
 44                         console.log(e);
 45                     });
 46             },
 47         }
 48     }
 49 </script>
 50 
 51 <style scoped>
 52 
 53 </style>
 54 
 55 
QuestionFollowButton.vue

show.blade.php

  1 @extends('layouts.app')
  2 @section('content')
  3     <div class="container">
  4         <div class="row">
  5             <div class="col-md-8 col-md offset-1">
  6                 {{--问题--}}
  7                 <div class="card">
  8                     <div class="card-header">
  9                         {{ $question->title }}
 10 
 11                         @foreach(['success','warning','danger'] as $info)
 12                             @if(session()->has($info))
 13                                 <div class="alert alert-{{$info}}">{{ session()->get($info) }}</div>
 14                             @endif
 15                         @endforeach
 16 
 17                         @can('update',$question)
 18                             <a href="{{ route('questions.edit',$question) }}" class="btn btn-warning">编辑</a>
 19                         @endcan
 20 
 21                         @can('destroy',$question)
 22                             <form action="{{ route('questions.destroy',$question) }}" method="post">
 23                                 @csrf
 24                                 @method('DELETE')
 25                                 <button type="submit" class="btn btn-danger">删除</button>
 26                             </form>
 27                         @endcan
 28 
 29                         @forelse($question->topics as $topic)
 30                             <button class="btn btn-secondary float-md-right m-1">{{ $topic->name }}</button>
 31                         @empty
 32                             <p class="text text-warning float-md-right"> "No Topics"</p>
 33                         @endforelse
 34 
 35                         <p class="text text-info float-md-right"> 已有{{ count($question->answers) }}个回答</p>
 36 
 37                     </div>
 38                     <div class="card-body">
 39                         {!! $question->content !!}
 40                     </div>
 41                 </div>
 42 
 43 
 44                 {{--回答提交form--}}
 45                 {{--只有登录用户可以提交回答--}}
 46                 @if(auth()->check())
 47                     <div class="card mt-2">
 48                         <div class="card-header">
 49                             提交回答
 50                         </div>
 51                         <div class="card-body">
 52                             <form action="{{ route('answers.store',$question) }}" method="post">
 53                             @csrf
 54                             <!-- 回答编辑器容器 -->
 55                                 <script id="container" name="content" type="text/plain"
 56                                         style="width: 100%;height: 200px">{!! old('content') !!}</script>
 57                                 <p class="text text-danger"> @error('content') {{ $message }} @enderror </p>
 58                                 <!--提交按钮-->
 59                                 <button type="submit" class="btn btn-primary float-md-right mt-2">提交回答</button>
 60                             </form>
 61                         </div>
 62                     </div>
 63                 @else
 64                     {{--显示请登录--}}
 65                     <a href="{{ route('login') }}" class="btn btn-success btn-block mt-4">登录提交答案</a>
 66                 @endif
 67                 {{--展示答案--}}
 68                 @forelse($question->answers as $answer)
 69                     <div class="card mt-4">
 70                         <div class="card-header">
 71                             <div class="float-left">
 72                                 <img src="{{ $answer->user->avatar }}" class="img-thumbnail imgWrap"
 73                                      style="height: 50px" alt="{{ $answer->user->name }}">
 74                                 <span class="text text-info">{{ $answer->user->name }}</span>
 75                             </div>
 76                             <span class="float-right text text-info m-auto">{{ $answer->updated_at }}</span>
 77                         </div>
 78 
 79                         <div class="card-body">
 80                             {!!  $answer->content  !!}
 81                         </div>
 82                     </div>
 83 
 84                 @empty
 85 
 86                 @endforelse
 87             </div>
 88 
 89             <div class="col-md-3">
 90                 <div class="card">
 91                     <div class="card-header">
 92                         <h2> {{ $question->followers_count }}</h2>
 93                         <span>关注者</span>
 94                     </div>
 95 
 96                     <div class="card-body">
 97                         <div id="app">
 98                             <question-follow-button question="{{$question->id}}">
 99                             </question-follow-button>
100                         </div>
101                     </div>
102                 </div>
103             </div>
104         </div>
105     </div>
106 @endsection
107 @section('footer-js')
108     @include('questions._footer_js')
109 @endsection
110 
111 
show.blade.php

补充:

截取

API requests with axios always unauthorized with Laravel API 

I'm not using Passort or any library like that since it's an internal API serving only VueJS to obtain stuff from the database.

If the API is not stateless, meaning that the user is known to be logged in with a standard session cookie, then you can just use the default 'web' middleware for the API routes.

In the default RouteServiceProvider, change the mapApiRoutes function to use the web middleware instead:

protected function mapApiRoutes()
{
    Route::prefix('api')
        // ->middleware('api')
        ->middleware('web')
        ->namespace($this->namespace)
        ->group(base_path('routes/api.php'));
}

That being said, you should really put the API routes behind the default 'auth' middleware since they're not throttled by default.

In the routes/api.php file:

Route::group(['middleware' => 'auth'], function() {
    Route::get('/latest', 'InternalApiController@latest');
});

And if you want to ensure it's an AJAX request, you can create a simple middleware that checks that the request has the X-Requested-With header set to XMLHttpRequest.

class RequestIsAjax
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (!$request->ajax()) {
            return redirect()->route('login.index');
        }

        return $next($request);
    }
}

And register it within the $routeMiddleware array inside the \App\Http\Kernel class.

protected $routeMiddleware = [
    'ajax' => \App\Http\Middleware\RequestIsAjax::class,
posted @ 2020-03-01 19:51  dzkjz  阅读(1156)  评论(0编辑  收藏  举报