Laravel 实战开发知乎:前后端分离 API token 认证
打赏作者

wl876645

大神更新好快~
如果我前端登录的组件用vue。 通过dingoapi到自己写的login方法验证。之后怎么才能在web上使用Auth:user()的方法获取到我api登录的用户。。就是我用api请求登录了验证成功,routes\web.php 用Auth::routes(); 用自带的http://localhost/login路由请求,他会提示已登录

JellyBool 回复 wl876645

依然是:

Auth::guard('api')->user();

可以再描述清楚一点么?我感觉看得有点乱

wenjie_zheng

api_token 应该是每次登录的时候会变吧?

JellyBool 回复 wenjie_zheng

不会的啊,因为这是存放在数据库中的,当然如果需要修改的话,可以提供一个用户重置 api_token 的功能

yuze wang

忍受不了,下一节的button 换到了下一行,犯病了,抢救中

JellyBool 回复 yuze wang

额。。。这个是因为 title 太长了吧

746019546

Vue.http.interceptors.push((request, next) => { request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken); request.headers.set('Authorization', Laravel.apiToken); next(); });

746019546

Vue.http.interceptors.push((request, next) => { request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken); request.headers.set('Authorization', Laravel.apiToken); next(); });
`
window.Laravel = <?php echo json_encode([
‘csrfToken’ => csrf_token(),
]); ?>;

	Laravel.apiToken = "{ Auth::check() ? 'Bearer '.Auth::user()->api_token : 'Bearer ' }";

`

我按照这个操作,还是提示401,看了好几遍视频了.结果还是一样

JellyBool 回复 746019546

仔细看看,这个基本上小问题,拼写或者是不太认真

xiaofengzhi 回复 JellyBool

我的也是,laravel5.4版本的不行啊,这个api_token通不过认证,能看一下5.4版本的怎么搞吗,差别有点大啊,要不您再搞个passport认证的小视频吧,这样也方便一些。

JellyBool 回复 xiaofengzhi

passport 视频不是有的么?

laixiaojie

请问一下群主~如果要实时刷新关注者的数据而不是刷新后才更新数据,基本思路是什么,感觉肯定是用Ajax,但是不太清楚怎么写,以前用Angular实现过类似的使用过.$watch来监控,然后是用的Angular来渲染的页面,这里用Laravel渲染不知道怎么实现,能给个思路吗

JellyBool 回复 laixiaojie

最简单的不就是 jquery 更新一下关注者的数字吧。或者你也可以使用 vuejs 解决

ukissme520

手机和pad无法播放,why?

JellyBool 回复 ukissme520

我这 iphone 6s plus 可以,有可能是 videojs 的bug

insertSweat

我添加api_token字段时,报以下错误

 SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '' fo
  r key 'users_api_token_unique' (SQL: alter table `users` add unique `users_
  api_token_unique`(`api_token`))
JellyBool 回复 insertSweat

应该是出现了两个 api_token 为空的情况,你手动设置一下就行

Ming Zhou 回复 JellyBool

我也出现这个报错,如果创建时指定api_token这个字段可为空的话,创建唯一索引就不会报错,如果不为空,就会因为都是空字符串而导致唯一索引创建失败。
请问一下怎么让空字符串也可null一样可以创建唯一索引呢

另外我发现dalao你在录视频的时候users表是一条记录,而我的表里是2条

JellyBool 回复 Ming Zhou

请问一下怎么让空字符串也可null一样可以创建唯一索引呢

这个是实现不了的吧。。

Ming Zhou 回复 JellyBool

确实,我试过在miagrate的时候设置api_token可以为nullable(),但是唯一索引没有创建,所以只能在表只有一条数据或者无数据时才能创建成功,要不然只能在表里各个数据都有api_token的情况下再添加唯一索引

mafeifan 回复 Ming Zhou

确实是bug…

leec

detached 那个字段是怎么出现的呀?

JellyBool 回复 leec

count(detached) ? 还是什么?

leec 回复 JellyBool

$followed[‘detached’] 这个似乎之前没有提到?

JellyBool 回复 leec

$followed[‘attach’] 这个应该有提到的吧,记得那时候稍微看了一下源码的,你看看 toggle() 的返回值就知道了

lg23 回复 JellyBool

attach 和 detach 一个是向关联数据表中添加一个是删除
但这个detached还没找到关于它的具体说明

天山网艾克

现在用户登陆的时候 show 页面没有报错,可以正常工作,如果没有登陆的情况下 出现401 的错误, 怎么解决? 求助 【“Unauthorized”】

JellyBool 回复 天山网艾克

你直接判断一下就好了嘛:

@if(Auth::check())
// 现实你的关注按钮
lg23

群主,我在执行 str_ramdom(60) 报下面的错误?百度并没有找到这个函数相关说明,
另外有英文的论坛说到这个是 laravel 自带的方法?求解
PHP Fatal error: Call to undefined function str_ramdom() in eval()'d code on line 1
文档中有说明,但命令行中执行却失败了

JellyBool 回复 lg23

str_random

试试

lg23 回复 JellyBool

好的,谢谢,错了一个字母

lg23

群主!实在无奈
count($followed[‘detached’]) 打印它的值,一直是在0和1之间变化。
但是这里的if判断没效果,居然一直递减,服务器重启几次,浏览器缓存也清过几次了,但就是这样,无奈呀!

Route::post('/question/follow', function (Request $request) {
	$user = Auth::guard('api')->user();
	$question = \App\Models\Question::find($request->get('question'));
	$followed = $user->followThis($question->id);

	// $followed 返回两数组结果集 detached attached
	// 对查询结果进行判断  非空 删除记录 改变状态
	if(count($followed['detached'] > 0)) {
        $question->decrement('followers_count');
		return response()->json(['followed' => false]);
	}

	// 否则 创建记录
    $question->increment('followers_count');
	// 返回状态
	return response()->json(['followed' => true]);

})->middleware('auth:api');

找到原因了,太不小心了,不好意思打扰

tczhangzhi

讲的好清楚!
不过好像有一个Passport是解决API认证的问题的?如果自己维护的话可能不太安全吧…毕竟被别人拿到的话就相当于拿到了账号密码…

JellyBool 回复 tczhangzhi

啊哈,对的。这个 api_token 是最基础的。

你可以使用 passport 来做

天山网艾克

这个视频内提到的 Auth::guard(‘api’)->user() 这里面的 api 这个token 在哪得到的 ? 我获取怎么会是 null 数据表中 添加了 api_token, 页面头部也加了 apiToken 等信息, 不知道什么情况, 也找不到 Auth::guard(‘api’) 这个赋值的代码, 真的很困扰啊 ,
如果我要重写一个后台管理的admin 的话 这个用api 的话 怎么设置》? 有没有知道的或者指导的?

JellyBool 回复 天山网艾克

你看看 config/auth.php 的配置文件就知道了啊;

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],

自己写 admin,这个视频就是 https://www.laravist.com/series/what-is-new-in-laravel-5-2-series/episodes/6

天山网艾克 回复 JellyBool

好的 知道怎么做了 谢谢

beaplat-61f

感觉passport是用来类似QQ登录那种第三方登录的,简单的登录认证没那个必要,不知道会不会理解错误

JellyBool 回复 beaplat-61f

就是这样的啊,OAuth 认证。。。

wuyutaott

有没有 passport package javascript 消费的视频

wuyutaott

我们给第三方 公司提供 api 支持 获取我公司的一些数据 是不是passport比较好呢。

JellyBool 回复 wuyutaott

是的,直接用 passport

zhouxiaoshuai3

我的5.3版本 在使用 api_token认证时,报错;
以下是我的代码:



Vue.http.interceptors.push((request, next) => {
    request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken);
    request.headers.set('Authorization', Laravel.apiToken);

    next();
});

 <script>
        window.Laravel = <?php echo json_encode([
            'csrfToken' => csrf_token(),
        ]); ?>
        Laravel.apiToken = "{Auth::check() ? 'Bearer '.Auth::user()->api_token : 'Bearer '}";
    </script>

报错信息是:

Uncaught SyntaxError: Unexpected identifier。。。
vue-resource.es2015.js?fc90:292 ReferenceError: Laravel is not defined。。。
Uncaught (in promise) ReferenceError: Laravel is not defined。。。
这让我很困惑,希望可以帮忙看一下,谢谢

wangshuaiws 回复 zhouxiaoshuai3

在?>后边加一个; 分号就行了。。。

zhouxiaoshuai3 回复 wangshuaiws

时间过去这么久了,我刚看到您的回复,bug已经解决。还是要感谢您,谢谢啦!!

noikiy

bootstrap.js 如下

let token = document.head.querySelector('meta[name="csrf-token"]');
let apiToken = document.head.querySelector('meta[name="apiToken"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
    window.axios.defaults.headers.common['Authorization'] = apiToken.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

app.blade.php如下

<meta name="csrf-token" content="{ csrf_token() }">
    <meta name="apiToken" content="{ Auth::check() ? 'Bearer '.Auth::user()->api_token : 'Bearer ' }">
aaa83351229 回复 noikiy

你漏了双括号

noikiy

laravel5.4的 改法如上

Annhunyss 回复 noikiy

按照提供的方法依然401错误。。。

noikiy 回复 Annhunyss

其实我是 laravel5.4的 但是我没有出现 401 我是出现的 50几 错误(记不清了)

biggerdong 回复 noikiy

500错误,什么都加了,就是获取不到user,laravel5.5版本还需要额外配置什么吗?

ienvenue 回复 biggerdong

‘’'
window.axios = require(‘axios’);

window.axios.defaults.headers.common[‘X-Requested-With’] = ‘XMLHttpRequest’;
window.axios.defaults.headers.common[‘Authorization’] = ‘Bearer ’ + Laravel.apiToken;
’’’

RoseEnd

添加api_token的时候报出如下错误:
ReferenceError: Laravel is not defined
at VueComponent.eval (eval at (http://zhihu.app/build/js/app-4f163be99d.js:83:1), :30:41)
at exec (eval at (http://zhihu.app/build/js/app-4f163be99d.js:146:1), :1149:29)
at VueComponent.eval (eval at (http://zhihu.app/build/js/app-4f163be99d.js:146:1), :1178:13)
at Promise ()
at new PromiseObj (eval at (http://zhihu.app/build/js/app-4f163be99d.js:146:1), :199:24)
at Client (eval at (http://zhihu.app/build/js/app-4f163be99d.js:146:1), :1142:16)
at Object.Http (eval at (http://zhihu.app/build/js/app-4f163be99d.js:146:1), :1399:12)
at Function.Http.(anonymous function) [as post] (eval at (http://zhihu.app/build/js/app-4f163be99d.js:146:1), :1438:20)
at VueComponent.mounted (eval at (http://zhihu.app/build/js/app-4f163be99d.js:115:1), :16:20)
at callHook (eval at (http://zhihu.app/build/js/app-4f163be99d.js:152:1), :2669:21)
怎么解决?

JellyBool 回复 RoseEnd

可否将相关代码贴出来看一下呢

RoseEnd

app.blade.php:

    <script>
        window.Laravel = <?php echo json_encode([
            'csrfToken' => csrf_token(),

        ]); ?>
        Laravel.apiToken = "{ Auth::check() ? 'Bearer '.Auth::user()->api_token : 'Bearer ' }";
    </script>

bootstrap.js:

    Vue.http.interceptors.push((request, next) => {
    request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken);
    request.headers.set('Authorization', Laravel.apiToken);

    next();
});

值可以获取到,但是就是不能点击关注按钮,

    <script>
        window.Laravel = {"csrfToken":"Aq2ztvmUKPzAr79MaqrRKbsQojLtLXqJv0kVKzvQ"}        
        Laravel.apiToken = "Bearer a7QDv8acuI13hOW7aYEITerZb7Q9B17IeAUjV43Vy1WQE7yVTEXipyFDv9ZU";
    </script>
JellyBool 回复 RoseEnd

好像也没有什么错啊。。。你在其他的 Vue.js 组件文件中有用到 Laravel 这个变量么

RoseEnd 回复 JellyBool

其他地方也没用到这个啊,我是按照你教程一步一步来的,//window.Laravel.apiToken = "{ Auth::check() ? 'Bearer '.Auth::user()->api_token : 'Bearer ' }";这段代码注释掉就正常

RoseEnd 回复 JellyBool

这个问题好奇怪!

JellyBool 回复 RoseEnd

我也觉得很奇怪

Aaron-wlh 回复 RoseEnd

在你的评论前面其实已经有人提出了这个问题,你可以去看看的

UncleCaozy 回复 RoseEnd

app.blade.php里面的?>后面加个分号

RoseEnd 回复 UncleCaozy

给你个大大的赞,你眼神果然毒辣,这都能发现!

UncleCaozy 回复 RoseEnd

前面有其他网友回复过的 我只是搬运一下 让你看见

UncleCaozy

我想问一下,后面的教程我都不用vue,只实现功能,可以吗

JellyBool 回复 UncleCaozy

思路都是一样的,你不用 vue 的话,随便使用一个你熟悉的就好

Aaron-wlh

如果是多用户认证登陆的话,比如前台使用的guard是user 后台使用的是admin,那么在api.php文件中使用Auth::guard(api)->user() 是否能分辨出是前台登陆用户还是后台登陆用户调用的接口? 我这么说不知道你名不明白我的意思==

JellyBool 回复 Aaron-wlh

我觉得应该是不能的

Aaron-wlh 回复 JellyBool

我也感觉不能,不过没有尝试过。。另外有个问题,还是多用户认证,auth:user和auth:admin这两个中间件,如果用户没有登陆应该跳转的是两个不同的登陆页面,这个应该是如何设置呢?是在Exceptions/Handler.php里面进行判断是哪种guard 然后根据不同的guard跳转到不同的登陆页面吗?

JellyBool 回复 Aaron-wlh

在 RedirectIfAuthenticated 的

 public function handle($request, Closure $next, $guard = null)

这里的 guard 的值就是 user 和 admin,判断一下应该就 OK

Aaron-wlh 回复 JellyBool
public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            return redirect('/');
        }

        return $next($request);
    }

这个文件的代码是判断是否登陆了,登陆了跳转到首页吧。 我想要知道的是 auth中间件在不同的参数(auth:user或auth:admin)下,如何跳转到不同的登陆界面。

JellyBool 回复 Aaron-wlh

那这个是在 Illuminate\Auth\Middleware\Authenticate 这里了。

public function handle($request, Closure $next, ...$guards)
    {
        $this->authenticate($guards);

        return $next($request);
    }
Aaron-wlh 回复 JellyBool

下面是App/Exceptions/Handle.php里的代码

protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }

        return redirect()->guest('login');
    }

是在这里面判断guard是哪种吧? 这一步判断我不知道怎么获取到guard的类型,有没有什么方法。

JellyBool 回复 Aaron-wlh

这个我不知道。。。在 Exception 中如何获取 guard

hellowords

你好,@JellyBool laravel 5.5 版本 使用视频中的方式api_token 认证报错了
bootstrap.js

// let token = document.head.querySelector('meta[name="csrf-token"]');
// let api_token = document.head.querySelector('meta[name="api_token"]');

let token = $('meta[name="csrf-token"]').attr('content');
let api_token = $('meta[name="api_token"]').attr('content');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
if(api_token){
    window.axios.defaults.headers.common['Authorization'] = api_token.content;
} else {
    console.error('API-Token');
}

vue组件

methods: {
            changeStatus(row){
                let  data = {
                    id:row.id,
                    status:row.bulletin_status
                };
              this.axios.post('/api/admin/change_status', data).then(response =>{
                 return response;
              });
            }

app.js

require('./bootstrap');

import axios from 'axios';
import VueAxios from 'vue-axios';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-default/index.css';
import DataTables from 'vue-data-tables';
/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */
Vue.use(ElementUI);
Vue.use(DataTables);
Vue.use(VueAxios, axios);

Vue.component('example', require('./components/Example.vue'));
Vue.component('TableSearch', require('./components/TableSearch.vue'));

const app = new Vue({
    el: '#app'
});

api.php 路由

Route::middleware('auth:api')->post('/admin/change_status', function (Request $request) {
    return $request;
});

不知道是什么原因

JellyBool 回复 hellowords

这个么?

window.axios.defaults.headers.common['Authorization'] = 'Bearer '+api_token.content;
hellowords 回复 JellyBool

感谢回复@JellyBool , 我加上‘Bearer ’ 之后还是报这个错误,不知道是不是因为我没有引入vue-resource这个包的原因

我看了上面朋友中的评论,在bootstrap.js文件中的代码

    Vue.http.interceptors.push((request, next) => {
    request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken);
    request.headers.set('Authorization', Laravel.apiToken);

    next();
});

我使用这种方式报错,interceptors 未定义好像,同时我在控制台NetWork中查看后台页面的数据,发现Request Headers中的参数没有api_tokn,并且X-CSRF-TOKEN:undefined

以下是blade模板中的相关代码

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{ csrf_token() }">
    <!-- API Token -->
    <meta name="api_token" content="{ Auth::check() ? 'Bearer '.Auth::user()->api_token : 'Bearer ' }">

两个值都能取到,在bootstrap.js中都能打出来的

使用5.4bootstrap.js中的方法成功了!
参考链接

扬扬的小天地

npm run dev后生成的app.js 太大, 1兆多, 看默认的也有1兆多, 怎么破

JellyBool 回复 扬扬的小天地

这个应该正常的吧,因为默认加载了 jquery 等。你 npm run prod 的话还好,因为这时候会压缩

liujun

api里面可以这样写吗?我测试了,但是有点小问题,不知道为什么

$user=Auth::guard('api')->user();

$json_data= $user->is_follow_question($request->get('question'));

return response()->json(['is_follow_question' => $json_data]);
JellyBool 回复 liujun

具体是什么问题呢?什么报错?

liujun

接上条,$json_data的取反去掉

不了了了了了之

是不是如果有人 for somehow 得到了这个api token,就可以模拟该用户登录?
那我是不是应该设置每次登出就更新api token?但是仍然会在此用户登录的时候有一定的风险啊?

tommywhywhy

没看懂。mark一下,回头在看几遍。