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 客户端的实现。

相关文章
|
1月前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
【10月更文挑战第2天】在当今互联网世界中,数据安全与隐私保护日益重要。Python 作为广泛应用于 Web 开发的语言,其认证机制也不断进化。OAuth 2.0 和 JSON Web Tokens (JWT) 成为当前最热门的安全认证方案,不仅保障数据安全传输,还简化了用户认证流程。本文将介绍 Python 如何结合 OAuth 2.0 和 JWT 打造安全高效的认证体系。
37 3
|
1月前
|
弹性计算 网络安全
阿里云国际OpenAPI多接口快速管理ECS服务器教程
阿里云国际OpenAPI多接口快速管理ECS服务器教程
|
13天前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
在当今互联网世界中,数据安全和隐私保护至关重要。Python 作为 Web 开发的主流语言,其认证机制也在不断进步。OAuth 2.0 和 JSON Web Tokens (JWT) 是当前最热门的安全认证方案,不仅保障数据安全传输,还简化用户认证流程。本文介绍如何在 Python 中结合 OAuth 2.0 和 JWT,打造一套既安全又高效的认证体系。通过 Flask-HTTPAuth 和 PyJWT 等库,实现授权和验证功能,确保每次请求的安全性和便捷性。
28 3
|
21天前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
1月前
|
JSON JavaScript 前端开发
使用 Node.js 和 Express 构建 RESTful API 服务器
【10月更文挑战第3天】使用 Node.js 和 Express 构建 RESTful API 服务器
|
2月前
|
安全 Java 数据安全/隐私保护
|
1月前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API服务器
【10月更文挑战第12天】使用JavaScript和Node.js构建简单的RESTful API服务器
17 0
|
2月前
|
JavaScript 前端开发
vue配合axios连接express搭建的node服务器接口_简单案例
文章介绍了如何使用Express框架搭建一个简单的Node服务器,并使用Vue结合Axios进行前端开发和接口调用,同时讨论了开发过程中遇到的跨域问题及其解决方案。
57 0
vue配合axios连接express搭建的node服务器接口_简单案例
|
2月前
|
JSON 安全 数据安全/隐私保护
Python 安全性大揭秘:OAuth 与 JWT,不只是认证,更是信任的传递
【9月更文挑战第4天】在数字化时代,确保应用安全至关重要。Python 作为广泛使用的编程语言,提供了强大的安全认证工具,如 OAuth 和 JWT。OAuth 是一种授权框架,允许第三方应用在有限权限下访问用户资源;JWT 则是一种自包含的数据传输格式,用于安全地传递声明。通过合理配置和使用这些技术,可以有效提升应用安全性,保障用户数据安全。正确管理和定期更新密钥、严格测试 JWT 的生成与验证等最佳实践,对于构建安全可靠的应用至关重要。不断学习新威胁,是维护应用安全的永恒课题。
52 2
|
3月前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
159 0

热门文章

最新文章