关于JWT(Json Web Token)的思考及使用心得

什么是JWT?

JWT(Json Web Token) 是一个开放的数据交换验证标准rfc7519 ( https://tools.ietf.org/html/rfc7519 ) ,一般用来做轻量级的API鉴权。由于许多API接口设计是遵循无状态的(比如Restful),所以JWT是Cookie Session这一套机制的替代方案。

组成

JWT由三部分组成头部(header)、载荷(payload)、签名(signature)。头部定义类型和加密方式;载荷部分放不是很重要的数据;签名使用定义的加密方式加密base64后的header和payload和一段你自己的加密key。最后的token由base64(header).base64(payload).base64(signatrue)组成。

使用方式

平时需要鉴权的接口需要传这个token,可以post字段提交,但是一般建议放在header头中 ,因为JWT一般配合https使用,这样就万无一失。

为什么安全?

首先token是服务端签发,然后验证时是用同样加密方式把header、payload和key再加密遍 然后看是不是和签名一致 如果不一致就说明token是非法的 这里主要靠的是加密(比如HS256)难以被攻破 至少目前吧 另外不得不说这里的加密对服务器来说是一个开销 这也是JWT的缺点

使用

我很早就听说过JWT 但那时候还没用上 感觉近几年前后端分离思想加速了JWT的使用 MVC前置到前端(VUE、REACT)后端只用提供API API强调无状态 自然而然使用了JWT这套方案

之前做小程序时 没有绝对使用JWT这套方案 我把它简化了下

最近开发的一套项目用的是Laravel做后端提供API服务,所以自然而然使用了JWT,使用的扩展是tymon/jwt-auth 关于这套扩展机制的具体使用可以参考这篇文章 写的很不错:https://laravel-china.org/articles/10885/full-use-of-jwt

项目使用时的具体细节

JWT三个时间概念

JWT有三个时间概念: 过期时间 宽限时间 刷新时间

上面那文章说token过了过期时间是不可刷新的,但其实是可以刷新的,我这边使用时可以(开启了黑名单机制和1min宽限时间) 但是过了刷新时间不能刷新这是肯定的

token刷新

可以借用laravel的中间件实现自动刷新 服务端判断过期了但是在刷新时间内主动刷新一次 并把新的token在Header头中返回给客户端

记住我功能

我感觉定义刷新时间(比如一个月) + token自动刷新机制这一套就是记住我功能

不能主动销毁Token

默认JWT这套方案好像不可以主动剔除用户的,因为服务端不会存token,只是验证。这和session不一样。 但是我感觉可以借用黑名单机制 来判断 中间件中判断该token在剔除黑名单中就销毁token并返回鉴权失败

单点登录

结合缓存比如redis存客户端标识是可以的

多套用户权限机制

比如客户端有用户端和商家端两套用户机制(不同表),可以在auth.php定义两个guard 分别指定对应的model Login逻辑单独写 比如auth(‘userApi’) auth(‘corpApi’) 可以拿到不同的guard 刷新中间件是可以共用的 不过router中的middleware方法可以指定具体的middleware

为什么不同权限体系的刷新中间件可以公用

感觉是jwt-auth实现了这机制 验证token时会根据sub字段判断 我base64decode两个token的payload后发现sub一个是1 一个是2

如果有问题欢迎指正!

Share Comments

如何调试Systemctl

有时候我们使用systemctl命令,比如systemctl start kube-apiserver时会提示错误:

1
2
[root@vultr kubenetes]# systemctl start kube-apiserver         
Job for kube-apiserver.service failed because the control process exited with error code. See "systemctl status kube-apiserver.service" and "journalctl -xe" for details.

根据提示,再运行systemctl status kube-apiserver.service 会提示类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
● kube-apiserver.service - Kubernetes API Service
Loaded: loaded (/usr/lib/systemd/system/kube-apiserver.service; disabled; vendor preset: disabled)
Active: failed (Result: start-limit) since Fri 2018-07-20 06:37:05 UTC; 1min 17s ago
Docs: https://github.com/GoogleCloudPlatform/kubernetes
Process: 8208 ExecStart=/usr/bin/kube-apiserver $KUBE_LOGTOSTDERR $KUBE_LOG_LEVEL $KUBE_ETCD_SERVERS $KUBE_API_ADDRESS $KUBE_API_PORT $KUBELET_PORT $KUBE_ALLOW_PRIV $KUBE_SERVICE_ADDRESSES $KUBE_ADMISSION_CONTROL $KUBE_API_ARGS (code=exited, status=203/EXEC)

Jul 20 06:37:05 vultr.guest systemd[1]: kube-apiserver.service: main process exited, code=exited, status=203/EXEC
Jul 20 06:37:05 vultr.guest systemd[1]: Failed to start Kubernetes API Service.
Jul 20 06:37:05 vultr.guest systemd[1]: Unit kube-apiserver.service entered failed state.
Jul 20 06:37:05 vultr.guest systemd[1]: kube-apiserver.service failed.
Jul 20 06:37:05 vultr.guest systemd[1]: kube-apiserver.service holdoff time over, scheduling restart.
Jul 20 06:37:05 vultr.guest systemd[1]: start request repeated too quickly for kube-apiserver.service
Jul 20 06:37:05 vultr.guest systemd[1]: Failed to start Kubernetes API Service.
Jul 20 06:37:05 vultr.guest systemd[1]: Unit kube-apiserver.service entered failed state.
Jul 20 06:37:05 vultr.guest systemd[1]: kube-apiserver.service failed.

注意看ExecStart

1
ExecStart=/usr/bin/kube-apiserver $KUBE_LOGTOSTDERR $KUBE_LOG_LEVEL $KUBE_ETCD_SERVERS $KUBE_API_ADDRESS $KUBE_API_PORT $KUBELET_PORT $KUBE_ALLOW_PRIV $KUBE_SERVICE_ADDRESSES $KUBE_ADMISSION_CONTROL $KUBE_API_ARGS

其实我们调试思想是,我们知道是这个kube-apiserver命令加上后续参数执行时出错了,所以只要我们手动运行一下这个命令就知道错在哪了,但是这里环境变量还是类似$KUBE_LOG_LEVEL这种变量,不是编译后的,不好调试运行。

这里有个技巧就是,编辑你system服务的service配置文件,在Service选中添加一项,类似:

1
ExecStartPre=/bin/bash -l -c 'echo "/usr/bin/kube-apiserver $YOUR_ARGS">/tmp/systemctl.debug'

实际运行时这里$YOUR_ASRGS替换为你自己在systemctl status后看到的参数。

然后systemctl daemon-reload,再start服务后,就可以运行 cat /tmp/systemctl.debug查看具体的运行命令,然后拷贝下来在命令行运行下就可以知道具体的错误了。

Share Comments

使用Docker compose编排Laravel项目

使用Docker compose编排Laravel应用

前言

Laravel官方开发环境推荐的是Homestead(其实就是一个封装好的Vagrant box),我感觉这个比较重,于是自己用Docker compose编排了一套开发环境,在这里分享下。

环境要求

先要安装好Docker 和 Docker compose,而且Docker 仓库镜像最好换成国内的。一般地,我开发电脑上会运行一个Vagrant,然后再在里面运行Docker等应用。

主要思路

Docker官方推荐的是一个容器运行一个服务,所以会有Compose编排,各个服务间通过容器互联技术通信,比如Php服务连接Mysql只用把Host名写成容器名,内部会直接转换成具体ip。代码目录使用数据卷从容器内映射到宿主机,配置文件(Nginx等)也是通过数据卷映射到容器内。

实践

这套服务我已经封装好了,平时用的话只用clone下来直接使用,我这里主要讲下实现思路。

项目地址:https://github.com/rootrl/php-environment-with-docker

我的项目目录结构:

php-environment-with-docker/
├── bin
│ ├── composer
│ ├── getContainerIp
│ └── php
├── conf
│ ├── nginx
│ │ └── conf.d
│ │ └── nginx.conf
│ └── redis
│ └── redis.conf
├── docker-compose.yaml
├── Dockerfile.php
├── LICENSE
├── README.MD
└── start

  • bin 这里面都是封装的命令行工具,其实也是Docker容器服务,只不过他们都是用完即走的服务。
  • conf 该目录都是应用的配置目录,会使用Volumn映射到容器内
  • docker-composer.yaml compose 的编排文件,下面会具体讲到
  • Dockerfile.php php的镜像构建(里面会有一些定制,比如改dns,装特殊扩展)
  • start 运行./start就可以启动所有服务,重启也可以运行此命令

docekr-compose.yaml

此文件是compose的编排文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
version: '2'

services:
nginx:
depends_on:
- "php"

image: "nginx"

volumes:
- "$PWD/conf/nginx/conf.d:/etc/nginx/conf.d"
- "$PWD/www:/usr/share/nginx/html"
ports:
- "8888:80"
networks:
- oa-network
container_name: "oa-nginx"
command: /bin/bash -c "mkdir -p /var/www && ln -s /usr/share/nginx/html /var/www && nginx -g 'daemon off;'"
php:
image: "oa-php-fpm"
build:
context: .
dockerfile: "Dockerfile.php"
networks:
- oa-network
container_name: "oa-php-fpm"
volumes:
- "$PWD/www:/var/www/html"

mysql:
image: mysql:5.7
volumes:
- "$PWD/db_data:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: oa
MYSQL_USER: oa
MYSQL_PASSWORD: oa123
ports:
- "3306:3306"
networks:
- oa-network
container_name: "oa-mysql"

redis:
image: "redis"
ports:
- "6379:6379"
networks:
- oa-network
volumes:
- "$PWD/conf/redis/redis.conf:/usr/local/etc/redis/redis.conf"
container_name: "oa-redis"

networks:
oa-network:
driver: bridge

这里定义了php-fpm、nignx、mysql、redis四个服务(如果需要其他服务,自行添加)。然后定义了一个公共的networks,这样容器内都可以很方便地进行通信。

比如nginx.conf中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/public/$fastcgi_script_name;
include fastcgi_params;
}
}

这里与php-fpm的连接方式:php:9000

Dockerfile.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM php:7.2-fpm
Run echo "nameserver 223.5.5.5" >> /etc/resolv.conf \
&& echo "nameserver 223.6.6.6" >> /etc/resolve.conf \
&& apt-get update \
&& apt-get install -y \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install mysqli pdo_mysql \
&& pecl install swoole \
&& pecl install redis \
&& docker-php-ext-enable swoole redis

这是Php镜像构建,这里改了dns服务器,并安装了若干php扩展。

使用

启动

./start 启动所有服务

命令行

1
2
3
4
5

./bin/php -v

# Laravel artisan
./bin/php artisan

总结

具体可访问:https://github.com/rootrl/php-environment-with-docker

Share Comments