php 实现 jwt
介绍
JWT提供了一种创建和验证访问令牌的方法,而无需中央存储(如数据库)。
JWT使用JSON Web签名 和 公钥加密 来确定其有效性。
JWT 的数据结构
HEADER.PAYLOAD.SIGNATURE
中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的。
- Header(头部)
- Payload(负载)
- Signature(签名)
HEADER
{"typ": "JWT", "alg":"RS256"}
Header 部分是一个 JSON 对象,描述 JWT 的元数据。
- alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256)
- typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
Payload
{ "id": "394a71988caa6cc30601e43f5b6569d52cd7f6df", "jti": "394a71988caa6cc30601e43f5b6569d52cd7f6df", "iss": "issuer_id", "aud": "client_id", "sub": "user_id", "exp": 1483711650, "iat": 1483708050, "token_type": "bearer", "scope": "onescope twoscope" }
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。除了官方字段,还可以在这个部分定义私有字段.
- id - 令牌的内部id
- jti - 令牌的唯一令牌标识符
- iss - 发出令牌的服务器的ID
- aud - 请求令牌的客户端的ID
- sub - 释放令牌的用户的ID
- exp - 令牌过期时的UNIX时间戳
- iat - 创建令牌时的UNIX时间戳
- token_type - 那种令牌,将是持票人
- scope - 以空格分隔的范围列表,为其颁发令牌
Signature
是对前两部分的签名,防止数据篡改。
需要指定一个密钥(secret)。使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
Base64URL
Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。
JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。
JWT 的使用方式
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。
此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。
获取jwt,携带jwt获取资源,服务端进行鉴权。
JWT 的几个特点
- 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
- 不加密的情况下,不能将秘密数据写入 JWT。
- 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
- 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
- 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
- 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
创建公钥和私钥对
mkdir keys cd keys openssl genrsa -out privkey.pem 2048 openssl rsa -in privkey.pem -pubout -out pubkey.pem
认证token服务
<?php /** * Created by PhpStorm. * User: jinchunguang * Date: 19-8-23 * Time: 下午1:21 */ // token.php // error reporting (this is a demo, after all!) ini_set('display_errors',1); error_reporting(E_ALL); // Autoloading (composer is preferred, but for this example let's just do this) require_once('oauth2-server-php/src/OAuth2/Autoloader.php'); OAuth2\Autoloader::register(); // your public key strings can be passed in however you like // (there is a public/private key pair for testing already in the oauth library) $publicKey = file_get_contents('./keys/pubkey.pem'); $privateKey = file_get_contents('./keys/privkey.pem'); // create storage $storage = new OAuth2\Storage\Memory(array( 'keys' => array( 'public_key' => $publicKey, 'private_key' => $privateKey, ), // add a Client ID for testing 'client_credentials' => array( 'CLIENT_ID' => array('client_secret' => 'CLIENT_SECRET') ), )); $server = new OAuth2\Server($storage, array( 'use_jwt_access_tokens' => true, )); $server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage)); // minimum config // send the response $server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
启动服务
php -S localhost:3000 &
获取access_token
curl http://localhost:3000/token.php -u 'CLIENT_ID:CLIENT_SECRET' -d "grant_type=client_credentials" |jq % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 755 0 726 100 29 27923 1115 --:--:-- --:--:-- --:--:-- 30200 { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpZCI6ImZiNzhiMmJjMmYyOGQwYzZlYTVhN2Y5YTZhN2Y0NjhhZmQxZWY0ZGIiLCJqdGkiOiJmYjc4YjJiYzJmMjhkMGM2ZWE1YTdmOWE2YTdmNDY4YWZkMWVmNGRiIiwiaXNzIjoiIiwiYXVkIjoiQ0xJRU5UX0lEIiwic3ViIjpudWxsLCJleHAiOjE1NjY1NDIzNjEsImlhdCI6MTU2NjUzODc2MSwidG9rZW5fdHlwZSI6ImJlYXJlciIsInNjb3BlIjpudWxsfQ.mSaXWNRQzFgPW1BJtxb8zM1Ktu0LkNru5Wzi1m6PTEy59yVZ3T6MbwlQzp3WmP0H-6eZhcQHPw1G-AHSNoR3Di_4umyvmcjCiHCixBWlcHy2ELZkeXOTrjjaoEJ-vpH33qfV7hh47QUHyJPEKHi-WMMoFfjCyhWxdRY-slV7oiWu69IaWLT8KmEY2FspjcUlWnd0jy4FWwA5jmyEY4qdke0ux2YHAwtKxT-y_i_2qQTK6T5AHe2GMyCy3Vk2dp2voX3igxpNmQ9zTCHI66Brz5OWWbG9SQVPB8udxLh_hLsLLrhuh1Q3DoaAjGho7Z8nZ0DV4vx2FPQDmnYbm5m4YA", "expires_in": 3600, "token_type": "bearer", "scope": null }
资源服务器认证
<?php /** * Created by PhpStorm. * User: jinchunguang * Date: 19-8-23 * Time: 下午1:25 */ require_once('oauth2-server-php/src/OAuth2/Autoloader.php'); OAuth2\Autoloader::register(); /* for a Resource Server (minimum config) */ $publicKey = file_get_contents('./keys/pubkey.pem'); // no private key necessary $keyStorage = new OAuth2\Storage\Memory(array('keys' => array( 'public_key' => $publicKey, ))); $server = new OAuth2\Server($keyStorage, array( 'use_jwt_access_tokens' => true, )); // verify the JWT Access Token in the request if (!$server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) { exit("Failed"); } // 正常业务逻辑 echo "Success!";
cli访问
curl -i http://localhost:3000/resource.php\?access_token\=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpZCI6ImZiNzhiMmJjMmYyOGQwYzZlYTVhN2Y5YTZhN2Y0NjhhZmQxZWY0ZGIiLCJqdGkiOiJmYjc4YjJiYzJmMjhkMGM2ZWE1YTdmOWE2YTdmNDY4YWZkMWVmNGRiIiwiaXNzIjoiIiwiYXVkIjoiQ0xJRU5UX0lEIiwic3ViIjpudWxsLCJleHAiOjE1NjY1NDIzNjEsImlhdCI6MTU2NjUzODc2MSwidG9rZW5fdHlwZSI6ImJlYXJlciIsInNjb3BlIjpudWxsfQ.mSaXWNRQzFgPW1BJtxb8zM1Ktu0LkNru5Wzi1m6PTEy59yVZ3T6MbwlQzp3WmP0H-6eZhcQHPw1G-AHSNoR3Di_4umyvmcjCiHCixBWlcHy2ELZkeXOTrjjaoEJ-vpH33qfV7hh47QUHyJPEKHi-WMMoFfjCyhWxdRY-slV7oiWu69IaWLT8KmEY2FspjcUlWnd0jy4FWwA5jmyEY4qdke0ux2YHAwtKxT-y_i_2qQTK6T5AHe2GMyCy3Vk2dp2voX3igxpNmQ9zTCHI66Brz5OWWbG9SQVPB8udxLh_hLsLLrhuh1Q3DoaAjGho7Z8nZ0DV4vx2FPQDmnYbm5m4YA HTTP/1.1 200 OK Host: localhost:3000 Date: Fri, 23 Aug 2019 05:41:36 GMT Connection: close X-Powered-By: PHP/7.2.19-0ubuntu0.18.04.2 Content-type: text/html; charset=UTF-8 Success!%
相关推荐
-
php 实现 jwt2025-04-25 02:03:59
-
PHP导出PDF文件2025-04-25 01:46:22
-
PHP和HTML怎么结合生成PNG图片2025-04-25 01:25:05
-
nginx+njs 配置 模拟2025-04-25 01:01:51
-
nginx websocket 400 – 解决 nginx 反代 wss 出现的 400 错误
nginx websocket 400 – 解决 nginx 反代 wss 出现的 400 错误2025-04-25 00:05:03