API接口JWT方式的Token认证(上),服务器(Laravel)的实现

简介: API接口JWT方式的Token认证(上),服务器(Laravel)的实现 最近在开发一个 Android 程序,需要做用户登录和认证功能,另外服务器用的是 Laravel 框架搭建的。

API接口JWT方式的Token认证(上),服务器(Laravel)的实现

最近在开发一个 Android 程序,需要做用户登录和认证功能,另外服务器用的是 Laravel 框架搭建的。最终决定用 JWT 实现API接口的认证。

JWT 是 Json Web Tokens 的缩写,与传统 Web 的 Cookies 或者 Session 方式的认证不同的是,JWT 是无状态的,服务器上不需要对 token 进行存储,也不需要和客户端保持连接。而 JWT 的 token 分3个部分,首先是头部 ,表明这是一个JWT,并指明加密方式,第二部分是负载,其中可以包含 账户名、ID、邮箱等用户信息,同时也包含了token到期时间,以 Unix 时间戳的方式记录,头部和负载都会进行 base64 编码,最后一部分是签名,用来验证负载的信息是否正确。

服务器上会保存一个全局 JWT_SECRET ,用于生成 token 和验证 token。在用户登录成功后,服务器从数据库获取用户的相关信息,计算出 token 到期时间,生成头部和负载并编码,再将前面的内容使用 JWT_SECRET 进行加密,生成签名,最后将3个部分合并返回给客户端。

客户端访问需要认证的客户端时,在 Http 请求头部加上 Authrization 字段,内容为 Bearer 加 token。服务器收到请求后,利用 JWT_SECRET 验证 token 是否合法,从负载中提取到期时间确认 token 是否过期,再从 token 提取用户信息,与数据库进行对比。如果这些都通过的话就可以进行后续操作了。

这里指大概介绍一下 JWT 的特点和验证流程,更详细的介绍大家可以自行搜索,或者访问 https://jwt.io/introduction/ 。

先上代码:
https://github.com/zhongchenyu/jokes-laravel
因为后续代码可能会做重构,本文所介绍的代码保存在 demo2 分支,请 checkout demo2

服务器代码实现

我们从一个空的 Laravel 项目开始着手,这里假设通过 laravel new project_name 命令安装了一个空的项目,并且完成了初始化配置,已经可以访问一些简单的测试 API了。下面开始搞 JWT。

1. 安装 JWT 库

首先通过 composer 安装 PHP 的 JWT 库:
composer require tymon/jwt-auth 0.5.*

在 config/app.php 下添加 JWTAuthServiceProvider:

'providers' => [

        /*
         * Laravel Framework Service Providers...
         */              
        ...
        Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class
    ],

也是在 config/app.php 下,注册门面:

'aliases' => [
        ...
        'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
        'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
    ],

发布 JWT 的配置文件到 config/jwt.php :

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
  • 1

生成 JWT_SECRET,执行命令php artisan jwt:generate ,
会在 config/jwt.php 下生成'secret' => env('JWT_SECRET', 'UCDncib***wOY6gj0sD'), ,从 .env 的 JWT_SECRET 取值,如果没有再取后面的默认值,因为 config/jwt.php 是要随着 Git 版本发布的,所有最好在不同的环境上分别执行命令来生成 secret ,并且保持到 .env 文件中, .env 文件默认是 gitignore 的。

这样 JWT 库就安装好可以使用了。

2. 初始化数据库

首先创建好数据库,并修改 .env 文件中相关的值:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=jokes
DB_USERNAME=homestead
DB_PASSWORD=secret

这是数据库还是空的,没有任何 table,需要先进行数据库迁移。要做用户认证,肯定需要一个用户表,这个其实 Laravel 在已经帮我们做好的,在 database/migrations 下面已经生成了迁移文件:
migrations

看一下 create_users_table.php 的代码,创建一个 users 表,包含 id、name、email、password等列。

public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

执行php artisan migrate 命令就会在 Jokes 数据库下创建好 users
表了。

mysql> show columns from users;
+----------------+------------------+------+-----+---------+----------------+
| Field          | Type             | Null | Key | Default | Extra          |
+----------------+------------------+------+-----+---------+----------------+
| id             | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name           | varchar(255)     | NO   |     | NULL    |                |
| email          | varchar(255)     | NO   | UNI | NULL    |                |
| password       | varchar(255)     | NO   |     | NULL    |                |
| remember_token | varchar(100)     | YES  |     | NULL    |                |
| created_at     | timestamp        | YES  |     | NULL    |                |
| updated_at     | timestamp        | YES  |     | NULL    |                |
+----------------+------------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

对应的 Model 也已经自动创建好了,就是 app/User.php 文件。

3. 实现注册 API

users table 创建好后,当然是要生成 user 数据了,可以用 seeder 来生成测试用的 Faker 数据,初始代码也基本完成了 users 的 seeder。不过我们直接实现注册 API,通过 API 来创建数据。

在 Routes/api.php 下增加一条路由,这里我们使用的是 Dingo/Api :

$api->post('register', 'Auth\RegisterController@register');
  • 1

访问 register 路径,会调用 Auth\RegisterController.php 控制器下的 register 方法,看下代码:

protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6' //|confirmed' 
        ]);
    }

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

  public function register(Request $request)
  {
    $this->validator($request->all())->validate();

    $user = $this->create($request->all());
    $token = JWTAuth::fromUser($user);
    return ["token" => $token];
  }

其实大部分代码 Laravel 已经自动生成好了,但是由于原来的代码是用于 Web 注册的,注册成功后会重定向到登录页面,但是我们是给 API 做认证,所有就把这块代码改一下。
validator 方法是用来验证 Http 请求参数的。

'name' => 'required|string|max:255' 表示 name 是必须的,并且是最大长度255的字符串。

'email' => 'required|string|email|max:255|unique:users' 表示 email 是必须的,为最长255的邮箱格式的字符串,并且要和 users 数据库中的email不重复。

'password' => 'required|string|min:6' 表示 password是必须的,为最短6位的字符串,也可以加上 confirmed,表示需要二次确认,在请求参数中要在加上 password_confirmation ,并且和 password 是相同的才能通过验证,这里我们就先不用这个确认了。

create 方法将注册的用户信息写到数据库的 users table 中,其中 password 是经过加密存储的。

在 register 方法中, 先调用 validator,检查请求参数是否合法,通过后调用 create 将此用户数据写入数据库,在根据用户信息生成一个 token,返回给客户端。

测试一下,注册成功,返回 token:
这里写图片描述

mysql> select id,name,email,password from users where email = 'user6@user.com' ;
+----+---------+----------------+--------------------------------------------------------------+
| id | name    | email          | password                                                     |
+----+---------+----------------+--------------------------------------------------------------+
|  9 | user666 | user6@user.com | $2y$10$YCj55dl7ByyRP4x6znfM0.6Xsj9ScwF6d5czn1t4RZ59bOQgg0ST6 |
+----+---------+----------------+--------------------------------------------------------------+
1 row in set (0.00 sec)

4. 实现登录 API

首先在 Routes/api.php 下添加路由:
$api->get('login', 'Auth\AuthenticateController@authenticate');

看下 Auth\AuthenticateController 下的 authenticate 方法:

public function authenticate(Request $request)
  {   
    $credentials = $request->only('email', 'password');
    try {     
      if (! $token = JWTAuth::attempt($credentials)) {
        return response()->json(['error' => 'invalid_credentials'], 401);
      }
    } catch (JWTException $e) {
      return response()->json(['error' => 'could_not_create_token'], 500);
    }
    $user = User::where('email', $credentials['email'])->first();
    $userTransform = new UserTransformer();
    return ['user'=> $userTransform->transform($user), 'token' => $token];   
  }

代码也是基本初始化好了的,但是原来的代码登录后只返回 token,我们修改下,加上返回 User 信息。
先将 请求中的 email 和 password 存到 $credentials 中,再通过$token = JWTAuth::attempt($credentials) 检验 email 和 password,并尝试转换成 token,如果失败,则返回异常,如果成功则将 user 和 token 返回。

测试一下,用刚才的账号登录,成功获取到用户信息和 token:
这里写图片描述

5. 实现 token 认证

用户完成注册登录后,获取到 token,接下来就可以访问需要认证的 API 了,这里我们建一个简单的 API 来说明认证的实现方法。

假设用户需要获取通知信息 notices,服务器要求必须在登录后才能获取。

首先添加路由,这里用到了路由组和中间件(middleware):
在 app/kernel.php 文件下添加路由中间件:

protected $routeMiddleware = [
        ...
        'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
        'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
    ];
$api->group(['middleware' => 'jwt.auth', 'providers' => 'jwt'], function ($api) { //
    $api->get('user', 'UserController@getUserInfo');
    $api->get('notices', 'NoticeController@index');
  });

此 group 下的所有路由,都需要先通过中间件处理,这里用的是 jwt.auth,及只有通过 jwt 认证之后,才能继续后面的访问。

这里只用来测试,NoticeController 下的 index 方法的返回数据直接是一句话:

public function index()
  {
    return ["content" => "This notice can be seen only after Auth"];
  }

测试一下用正确的 token 访问,在 Header 添加 Authrization 项,记住在 token 前加 Bearer ,可以获取数据:
这里写图片描述

测试用非法 token 访问,返回400错误:
这里写图片描述

测试过期 token 访问, 返回401错误:
这里写图片描述

同时我们还添加了 user 路由,JWTAuth::parseToken()->authenticate() 通过 token 获取用户:

class UserController extends Controller{
  public function getUserInfo(Request $request)
  {
    $user = JWTAuth::parseToken()->authenticate();
    return ( new UserTransformer())->transform($user);
  }
}

请求中添加 Authrization头,测试,这个 API 不仅用来获取用户信息,也作为 客户端存储的 token是否有效的检测 API:
这里写图片描述

至此,服务器上的认证相关的 API 接口就都准备好了,下篇文章将讲 Android 客户端的实现。

相关文章
|
17天前
|
缓存 前端开发 API
API接口封装系列
API(Application Programming Interface)接口封装是将系统内部的功能封装成可复用的程序接口并向外部提供,以便其他系统调用和使用这些功能,通过这种方式实现系统之间的通信和协作。下面将介绍API接口封装的一些关键步骤和注意事项。
|
24天前
|
监控 前端开发 JavaScript
实战篇:商品API接口在跨平台销售中的有效运用与案例解析
随着电子商务的蓬勃发展,企业为了扩大市场覆盖面,经常需要在多个在线平台上展示和销售产品。然而,手工管理多个平台的库存、价格、商品描述等信息既耗时又容易出错。商品API接口在这一背景下显得尤为重要,它能够帮助企业在不同的销售平台之间实现商品信息的高效同步和管理。本文将通过具体的淘宝API接口使用案例,展示如何在跨平台销售中有效利用商品API接口,以及如何通过代码实现数据的统一管理。
|
1月前
|
安全 算法 API
产品经理必备知识——API接口
前言 在古代,我们的传输信息的方式有很多,比如写信、飞鸽传书,以及在战争中使用的烽烟,才有了著名的烽火戏诸侯,但这些方式传输信息的效率终究还是无法满足高速发展的社会需要。如今万物互联的时代,我通过一部手机就可以实现衣食住行的方方面面,比如:在家购物、远程控制家电、自动驾驶等等,背后都离不开我们今天要聊的API接口。
|
1月前
|
数据采集 JSON API
如何实现高效率超简洁的实时数据采集?——Python实战电商数据采集API接口
你是否曾为获取重要数据而感到困扰?是否因为数据封锁而无法获取所需信息?是否因为数据格式混乱而头疼?现在,所有这些问题都可以迎刃而解。让我为大家介绍一款强大的数据采集API接口。
|
1月前
|
安全 API 数据安全/隐私保护
API接口知识小结
应用程序接口API(Application Programming Interface),是提供特定业务输出能力、连接不同系统的一种约定。这里包括外部系统与提供服务的系统(中后台系统)或后台不同系统之间的交互点。包括外部接口、内部接口,内部接口又包括:上层服务与下层服务接口、同级接口。
|
2天前
|
API 开发者
邮件API接口使用的方法和步骤
AOKSEND指南:了解和使用邮件API接口,包括选择适合的接口(如AOKSEND、Mailgun、SMTP),获取访问权限,配置发件人、收件人及邮件内容,调用接口发送邮件,并处理返回结果,以高效集成邮件功能。
|
4天前
|
Java API Android开发
[NDK/JNI系列04] JNI接口方法表、基础API与异常API
[NDK/JNI系列04] JNI接口方法表、基础API与异常API
11 0
|
7天前
|
XML JSON API
快速淘宝商品详情页面API接口传输 php
PI(Application Programming Interface,应用程序接口)是一组预定义的函数、协议和工具,用于构建软件应用程序之间的交互。它允许不同的软件系统和应用通过统一的接口进行数据交换和通信
|
7天前
|
小程序 开发者
体验版小程序为何无法访问云端服务器后端接口(请求失败...(已完美解决附加图片))?
体验版小程序为何无法访问云端服务器后端接口(请求失败...(已完美解决附加图片))?
14 0
|
12天前
|
人工智能 API 开发者
免费使用Kimi的API接口,kimi-free-api真香
今年AI应用兴起,各类智能体涌现,但API免费额度有限。为解决这一问题,GitHub上的[kimi-free-api](https://github.com/LLM-Red-Team/kimi-free-api)项目提供了方便,支持高速流式输出、多轮对话等,与ChatGPT接口兼容。此外,还有其他大模型的免费API转换项目,如跃问StepChat、阿里通义Qwen等。该项目可帮助用户免费体验,通过Docker-compose轻松部署。只需获取refresh_token,即可开始使用。这个开源项目促进了AI学习和开发,为探索AI潜力提供了新途径。
232 2