老邓同志 發表於 2021-10-14 17:36:00

PHP代码篇(九)PHP接口开发如何使用JWT进行验证身份

<ul>
<li>前言</li>
</ul>
<p>    事情是这样的,在我进入目前公司的时候。因为公司是一家创业公司,所以在我进去的时候,里面的开发配置就是web前端,ui设计,加我PHP后台各一个。接手的是一个公益小程序,业务倒是不怎么复杂,负责人说这个项目是之前委托外包公司开发的,用的是uniapp开发的小程序,基于ThinkPHP6.0做的接口开发,和一个CatchAdmin开发的管理后台。公司主要业务这个上面都已经开发完了,但是由于微信小程序的一些功能的限制,我们想把目前的这个项目转移到微信公众号上面,就是改成H5开发的模式。</p>
<ul>
<li>八月的雨</li>
</ul>
<p>    当时负责人,说前端可能需要重写,因为之前是小程序,现在是H5嘛,前端是一个刚服役退伍的年轻小伙子,看了下说,用uniapp开发,到时候打包成H5就可以了,基本问题不大,于是就卡卡的开始写静态页面了,说一周写完静态页面。</p>
<p>    到后台这边,就有两个选择了。因为不管怎么变,最终还是做接口开发。所以有两个选择,要么在原来外包公司开发的api上修改,做二开,这样应该基本改下登陆注册方式,和部分业务逻辑即可;听取负责人的态度和想法目前对外包公司开发的api项目主要有如下不满点:</p>
<p>      1、说这个外包公司开发的项目,里面有很多业务逻辑和现在他们想要不一样;</p>
<p>      2、有些功能业务,实际上还没开发完,或者有些开发完了,还没测试,有很多问题;</p>
<p>      3、对这个项目的完成度,负责人,自己也不是完全清楚;</p>
<p>    根据负责人的描述,再结合自己这几天看项目源代码,发现外包写的代码确实比较差,看出来,虽然是用的tp框架,但是写的基本上非常赶。有些接口,基本上是写完,就没有具体测试。还有最主要的就是,基本所有的业务代码逻辑承载都在一个controller里面,非常不适合后期的迭代维护。所以第二种选择就是,直接进行重写,根据负责人对与项目的业务要求,直接重新设计数据库,重写api接口。</p>
<ul>
<li>八月中下旬的选择</li>
</ul>
<p>    当时我的想法,当然是重写是最好,原因如下:</p>
<p>      1、这个原有项目真实逻辑没有开发人员真实了解(就是我来的时候,都没有对接人);</p>
<p>      2、这个项目本身业务逻辑比较单一,而且二期改版有十分大;</p>
<p>      3、如果二开,我既要去熟悉旧有代码,还要修改,对于新功能的开发,还需要切合旧有逻辑;</p>
<p>    想到如上,确实还是直接重写最好,这样,一个是代码的标准,质量,逻辑都在自己的掌握之中,也便于后期的工作。</p>
<ul>
<li>我承认自己有赌的成分</li>
</ul>
<p>    重写的原因说来,一条一条的,清晰完整。但是在做的时候,因为之前一般进入公司的,去的时候,公司项目基本上已经上线了,我们主要是做维护,和调优;自己主导写一个完整的项目,还没有;而且之前大多是做前后端不分离的方式,像这种做接口开发,前后端分离的方式,也有点欠缺理解。</p>
<p>    于是入职这一周时间,我一直在重复一个流程:</p>
<p>      1、想一个自己能说的通的开发方案,在脑子里过几遍;</p>
<p>      2、如果感觉没问题,就下载相应框架源码,搭建demo,开始按方案写代码逻辑。在写的过程中,如果出现方案外的问题,就想办法解决;</p>
<p>      3、如果方案外的问题实在,无力解决,就只能推倒当前方案;</p>
<p>    最后又回到流程1,这样我记得有很多次了,当时感觉已经很奔溃了,有点后悔,因为我目前的问题主要是对于这个接口验证JWT不是特理解,虽然现在写的时候,已经明白。但是当时是真的,有点晕(今天是10月14日,入职是8月12日)。这里说后悔是什么意思呢,因为在上一家公司做的时候,我们有做过一个新项目,就是前后端分离的模式。前端用的是vue,后端纯接口开发,接口验证用的就是jwt。但是当时基础部分是另外一个同事写的,我只负责写业务端,像登录注册,验证,板块设计都是他做的。记得当时是有在看他验证这块是怎么写的,有不懂的也有当面问过,但是还是没有具体弄明白,后来想着如果后面需要自己处理这块,再说,我承认当时确实有赌的成份,现在果然赌输了。</p>
<p>    人生很多事,就是这样,有的时候,不知道珍惜,到后来需要自己独自面对的时候,就要多劳力了。经历了上面的不断的试错和尝试,时间在一天一天过去,但是我这边进展,还是一直没有。每次项目进度开会,明显感觉到,像是就我这边进度缓慢。但是有一点可以确认的是,每次在自己的演示中,虽然方案行不通,回到了最初点,但是感觉对于jwt的理解和架构的设计,有了一些实际的理解。我有一种感觉,应该快出来了。果然在一个现在已经忘记了时间上,出来了。</p>
<ul>
<li>言归正传</li>
</ul>
<p>  PHP通过jwt实现接口验证,设计思路如下:</p>
<p>    1、先定义controller控制器基类Base.php,作用是继承改类的,都需要进行token验证;</p>
<p>    2、在定义一个前端api基类IndexBase.php,作为一个中间层,里面存放验证后token里面的用户信息</p>
<p>    3、书写PHP实现jwt基类PhpJwt.php,改类主要是获取token,和验证token;</p>
<p>  上面三者的关系是,IndexBase.php继承Base.php继承PhpJwt.php。</p>
<p>  前端与后端验证逻辑如下:</p>
<p>    1、前端请求接口,后台验证token;</p>
<p>    2、没有token,给出提示,前端输入账号密码(微信公众号类,直接通过非静默授权,获取用户openid),进行验证用户信息。验证通过后,将用户uid加载到jwt载荷中,生成token。一个验证toekn(过期时间比较短),一个刷新token(过期时间较长,用于避免每次段时间内,用户重复登录)。</p>
<p>    3、前端通过登录拿到两个token后,存起来。然后在请求需要验证的接口时,在header头部加入参数authorization:用户toekn;来进行验证,后台通过token来解析出当前请求用户的信息。</p>
<ul>
<li>核心代码</li>
</ul>
<p>    1、PhpJwt.php</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* PHP实现jwt
*
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
namespace lib;

</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> PhpJwt {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">头部</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$header</span> = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)">(
      </span>'alg'=&gt;'HS256', <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成signature的算法</span>
      'typ'=&gt;'JWT'<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">类型</span>
<span style="color: rgba(0, 0, 0, 1)">    );

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用HMAC生成信息摘要时所使用的密钥 md5('jjgw2021')</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$key</span> = '99dc2d62ab85bcd9185f3e9324db5567'<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">md5('jjgw2021admin')</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$admin_key</span> = '12d44e568140bf62d84d9cb3e20b1103'<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">请求jwt 过期时间 2小时(上线后改为10分钟)</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$request_expect</span> = 3600<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">刷新jwt 过期时间 24小时</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$refresh_expect</span> = 86400<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$admin_request_expect</span> = 1800<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$admin_refresh_expect</span> = 7200<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">判断是否后端token</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$is_admin</span> = 'is_admin'<span style="color: rgba(0, 0, 0, 1)">;


    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">** 获取jwt token
   * @param array $payload jwt载荷格式如下非必须
   * [
   * 'iss'=&gt;'jwt_admin', //该JWT的签发者
   * 'iat'=&gt;time(), //签发时间
   * 'exp'=&gt;time()+7200, //过期时间
   * 'nbf'=&gt;time()+60, //该时间之前不接收处理该Token
   * 'sub'=&gt;'www.admin.com', //面向的用户
   * 'jti'=&gt;md5(uniqid('JWT').time()) //该Token唯一标识
   * ]
   * @param int $refresh 是否刷新token 1是
   * @param int $is_admin 是否后台调用 1是 0 admin
   * @return string
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">function</span> getToken(<span style="color: rgba(0, 0, 255, 1)">array</span> <span style="color: rgba(128, 0, 128, 1)">$payload</span>, <span style="color: rgba(128, 0, 128, 1)">$refresh</span> = 0, <span style="color: rgba(128, 0, 128, 1)">$is_admin</span> = 0<span style="color: rgba(0, 0, 0, 1)">)
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$exp</span> = <span style="color: rgba(128, 0, 128, 1)">$refresh</span> ? (<span style="color: rgba(128, 0, 128, 1)">$is_admin</span> ? self::<span style="color: rgba(128, 0, 128, 1)">$admin_refresh_expect</span> : self::<span style="color: rgba(128, 0, 128, 1)">$refresh_expect</span>) : (<span style="color: rgba(128, 0, 128, 1)">$is_admin</span> ? self::<span style="color: rgba(128, 0, 128, 1)">$admin_request_expect</span> : self::<span style="color: rgba(128, 0, 128, 1)">$request_expect</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(128, 0, 128, 1)">$load</span> =<span style="color: rgba(0, 0, 0, 1)"> [
            </span>'iat' =&gt; <span style="color: rgba(0, 128, 128, 1)">time</span>(),
            'exp' =&gt; <span style="color: rgba(0, 128, 128, 1)">time</span>() + <span style="color: rgba(128, 0, 128, 1)">$exp</span>,
            'jti' =&gt; <span style="color: rgba(0, 128, 128, 1)">md5</span>(<span style="color: rgba(0, 128, 128, 1)">uniqid</span>('JWT').<span style="color: rgba(0, 128, 128, 1)">time</span><span style="color: rgba(0, 0, 0, 1)">())
      ];
      </span><span style="color: rgba(128, 0, 128, 1)">$payload</span> = <span style="color: rgba(0, 128, 128, 1)">array_merge</span>(<span style="color: rgba(128, 0, 128, 1)">$load</span>, <span style="color: rgba(128, 0, 128, 1)">$payload</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(128, 0, 128, 1)">$key</span> = (<span style="color: rgba(128, 0, 128, 1)">$is_admin</span> == 1 ? self::<span style="color: rgba(128, 0, 128, 1)">$admin_key</span> : self::<span style="color: rgba(128, 0, 128, 1)">$key</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(128, 0, 128, 1)">$base64header</span> = self::base64UrlEncode(json_encode(self::<span style="color: rgba(128, 0, 128, 1)">$header</span>,<span style="color: rgba(0, 0, 0, 1)">JSON_UNESCAPED_UNICODE));
      </span><span style="color: rgba(128, 0, 128, 1)">$base64payload</span> = self::base64UrlEncode(json_encode(<span style="color: rgba(128, 0, 128, 1)">$payload</span>,<span style="color: rgba(0, 0, 0, 1)">JSON_UNESCAPED_UNICODE));
      </span><span style="color: rgba(128, 0, 128, 1)">$token</span> = <span style="color: rgba(128, 0, 128, 1)">$base64header</span>.'.'.<span style="color: rgba(128, 0, 128, 1)">$base64payload</span>.'.'.self::signature(<span style="color: rgba(128, 0, 128, 1)">$base64header</span>.'.'.<span style="color: rgba(128, 0, 128, 1)">$base64payload</span>,<span style="color: rgba(128, 0, 128, 1)">$key</span>,self::<span style="color: rgba(128, 0, 128, 1)">$header</span>['alg'<span style="color: rgba(0, 0, 0, 1)">]);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$token</span><span style="color: rgba(0, 0, 0, 1)">;
    }


</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * 验证token是否有效,默认验证exp,nbf,iat时间
   * @param string $Token 需要验证的token
   * @return array
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">function</span> verifyToken(<span style="color: rgba(128, 0, 128, 1)">$Token</span><span style="color: rgba(0, 0, 0, 1)">)
{
      </span><span style="color: rgba(128, 0, 128, 1)">$tokens</span> = <span style="color: rgba(0, 128, 128, 1)">explode</span>('.', <span style="color: rgba(128, 0, 128, 1)">$Token</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 128, 128, 1)">count</span>(<span style="color: rgba(128, 0, 128, 1)">$tokens</span>) != 3<span style="color: rgba(0, 0, 0, 1)">){
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
                </span>'code' =&gt; 100,
                'msg' =&gt; '验证失败'<span style="color: rgba(0, 0, 0, 1)">
            ];
      }
   
      </span><span style="color: rgba(0, 0, 255, 1)">list</span>(<span style="color: rgba(128, 0, 128, 1)">$base64header</span>, <span style="color: rgba(128, 0, 128, 1)">$base64payload</span>, <span style="color: rgba(128, 0, 128, 1)">$sign</span>) = <span style="color: rgba(128, 0, 128, 1)">$tokens</span><span style="color: rgba(0, 0, 0, 1)">;
   
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取jwt算法</span>
      <span style="color: rgba(128, 0, 128, 1)">$base64decodeheader</span> = json_decode(self::base64UrlDecode(<span style="color: rgba(128, 0, 128, 1)">$base64header</span>),<span style="color: rgba(0, 0, 0, 1)"> JSON_OBJECT_AS_ARRAY);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$base64decodeheader</span>['alg'<span style="color: rgba(0, 0, 0, 1)">])){
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
                </span>'code' =&gt; 100,
                'msg' =&gt; '验证失败'<span style="color: rgba(0, 0, 0, 1)">
            ];
      }
      </span><span style="color: rgba(128, 0, 128, 1)">$payload</span> = json_decode(self::base64UrlDecode(<span style="color: rgba(128, 0, 128, 1)">$base64payload</span>),<span style="color: rgba(0, 0, 0, 1)"> JSON_OBJECT_AS_ARRAY);
      </span><span style="color: rgba(128, 0, 128, 1)">$key</span> = !<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$payload</span>) ? self::<span style="color: rgba(128, 0, 128, 1)">$admin_key</span> : self::<span style="color: rgba(128, 0, 128, 1)">$key</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">签名验证</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (self::signature(<span style="color: rgba(128, 0, 128, 1)">$base64header</span> . '.' . <span style="color: rgba(128, 0, 128, 1)">$base64payload</span>, <span style="color: rgba(128, 0, 128, 1)">$key</span>, <span style="color: rgba(128, 0, 128, 1)">$base64decodeheader</span>['alg']) !== <span style="color: rgba(128, 0, 128, 1)">$sign</span><span style="color: rgba(0, 0, 0, 1)">){
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
                </span>'code' =&gt; 100,
                'msg' =&gt; '签名验证失败'<span style="color: rgba(0, 0, 0, 1)">
            ];
      }
   
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">签发时间大于当前服务器时间验证失败</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">isset</span>(<span style="color: rgba(128, 0, 128, 1)">$payload</span>['iat']) &amp;&amp; <span style="color: rgba(128, 0, 128, 1)">$payload</span>['iat'] &gt; <span style="color: rgba(0, 128, 128, 1)">time</span><span style="color: rgba(0, 0, 0, 1)">()) {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
                </span>'code' =&gt; 100,
                'msg' =&gt; '签发时间大于当前服务器时间,验证失败'<span style="color: rgba(0, 0, 0, 1)">
            ];
      }
   
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">过期时间小宇当前服务器时间验证失败</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">isset</span>(<span style="color: rgba(128, 0, 128, 1)">$payload</span>['exp']) &amp;&amp; <span style="color: rgba(128, 0, 128, 1)">$payload</span>['exp'] &lt; <span style="color: rgba(0, 128, 128, 1)">time</span><span style="color: rgba(0, 0, 0, 1)">()) {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
                </span>'code' =&gt; 200,
                'msg' =&gt; '已过期'<span style="color: rgba(0, 0, 0, 1)">
            ];
      }
   
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">该nbf时间之前不接收处理该Token</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">isset</span>(<span style="color: rgba(128, 0, 128, 1)">$payload</span>['nbf']) &amp;&amp; <span style="color: rgba(128, 0, 128, 1)">$payload</span>['nbf'] &gt; <span style="color: rgba(0, 128, 128, 1)">time</span><span style="color: rgba(0, 0, 0, 1)">()) {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
                </span>'code' =&gt; 100,
                'msg' =&gt; '验证失败'<span style="color: rgba(0, 0, 0, 1)">
            ];
      }
   
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
            </span>'code' =&gt; 0,
            'msg' =&gt; '验证成功',
            'data' =&gt;<span style="color: rgba(128, 0, 128, 1)">$payload</span><span style="color: rgba(0, 0, 0, 1)">
      ];
}




</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * base64UrlEncodehttps://jwt.io/ 中base64UrlEncode编码实现
   * @param string $input 需要编码的字符串
   * @return string
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">function</span> base64UrlEncode(<span style="color: rgba(128, 0, 128, 1)">$input</span><span style="color: rgba(0, 0, 0, 1)">)
{
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 128, 128, 1)">str_replace</span>('=', '', <span style="color: rgba(0, 128, 128, 1)">strtr</span>(<span style="color: rgba(0, 128, 128, 1)">base64_encode</span>(<span style="color: rgba(128, 0, 128, 1)">$input</span>), '+/', '-_'<span style="color: rgba(0, 0, 0, 1)">));
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现
   * @param string $input 需要解码的字符串
   * @return bool|string
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">function</span> base64UrlDecode(<span style="color: rgba(128, 0, 128, 1)">$input</span><span style="color: rgba(0, 0, 0, 1)">)
{
      </span><span style="color: rgba(128, 0, 128, 1)">$remainder</span> = <span style="color: rgba(0, 128, 128, 1)">strlen</span>(<span style="color: rgba(128, 0, 128, 1)">$input</span>) % 4<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$remainder</span><span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(128, 0, 128, 1)">$addlen</span> = 4 - <span style="color: rgba(128, 0, 128, 1)">$remainder</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(128, 0, 128, 1)">$input</span> .= <span style="color: rgba(0, 128, 128, 1)">str_repeat</span>('=', <span style="color: rgba(128, 0, 128, 1)">$addlen</span><span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 128, 128, 1)">base64_decode</span>(<span style="color: rgba(0, 128, 128, 1)">strtr</span>(<span style="color: rgba(128, 0, 128, 1)">$input</span>, '-_', '+/'<span style="color: rgba(0, 0, 0, 1)">));
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * HMACSHA256签名https://jwt.io/ 中HMACSHA256签名实现
   * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
   * @param string $key
   * @param string $alg算法方式
   * @return mixed
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">function</span> signature(<span style="color: rgba(128, 0, 128, 1)">$input</span>, <span style="color: rgba(128, 0, 128, 1)">$key</span>, <span style="color: rgba(128, 0, 128, 1)">$alg</span><span style="color: rgba(0, 0, 0, 1)"> )
{
      </span><span style="color: rgba(128, 0, 128, 1)">$alg_config</span> = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)">(
            </span>'HS256'=&gt;'sha256'<span style="color: rgba(0, 0, 0, 1)">
      );
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> self::base64UrlEncode(hash_hmac(<span style="color: rgba(128, 0, 128, 1)">$alg_config</span>[<span style="color: rgba(128, 0, 128, 1)">$alg</span>], <span style="color: rgba(128, 0, 128, 1)">$input</span>, <span style="color: rgba(128, 0, 128, 1)">$key</span>,<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">));
}
}
</span></pre>
</div>
<p>    2、Base.php</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* @Fun: 控制器基类
* @User: JessieK
* @Date: 2021-08-19 18:06:47
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
namespace app\controller;

</span><span style="color: rgba(0, 0, 255, 1)">use</span><span style="color: rgba(0, 0, 0, 1)"> app\BaseController;
</span><span style="color: rgba(0, 0, 255, 1)">use</span><span style="color: rgba(0, 0, 0, 1)"> think\facade\Request;
</span><span style="color: rgba(0, 0, 255, 1)">use</span><span style="color: rgba(0, 0, 0, 1)"> lib\PhpJwt;

</span><span style="color: rgba(0, 0, 255, 1)">class</span> Base <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> BaseController
{
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">会员uid</span>
    <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(128, 0, 128, 1)">$uid</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">会员unionid</span>
    <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(128, 0, 128, 1)">$unionid</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">会员openid</span>
    <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(128, 0, 128, 1)">$openid</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">jwt</span>
    <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(128, 0, 128, 1)">$payload</span><span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> __construct()
    {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">更新中
      // $this-&gt;apiResult(-100, '网站正在火速更新中,请稍后---');
      // $controller = strtolower(Request::controller());</span>
      <span style="color: rgba(128, 0, 128, 1)">$action</span> = <span style="color: rgba(0, 128, 128, 1)">strtolower</span>(Request::<span style="color: rgba(0, 0, 0, 1)">action());
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 128, 128, 1)">in_array</span>(<span style="color: rgba(128, 0, 128, 1)">$action</span>, ['wechatlogin', 'index', 'coc', 'arealist', 'uploadimg', 'uploadimgstring'<span style="color: rgba(0, 0, 0, 1)">])){
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">验证token</span>
            <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">checkToken();
      }
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 128, 128, 1)">in_array</span>(<span style="color: rgba(128, 0, 128, 1)">$action</span>, ['wechatlogin', 'index', 'coc', 'arealist', 'uploadimg', 'getjwt', 'uploadimgstring'<span style="color: rgba(0, 0, 0, 1)">])){
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">验证sign
            // $this-&gt;verifySign();</span>
<span style="color: rgba(0, 0, 0, 1)">      }
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * 验证token
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> checkToken()
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$token</span> = <span style="color: rgba(0, 0, 255, 1)">empty</span>(Request::<span style="color: rgba(0, 128, 128, 1)">header</span>()['authorization']) ? '' : Request::<span style="color: rgba(0, 128, 128, 1)">header</span>()['authorization'<span style="color: rgba(0, 0, 0, 1)">];
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(128, 0, 128, 1)">$token</span><span style="color: rgba(0, 0, 0, 1)">){
            </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(-100, 'Authorization不能为空'<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(128, 0, 128, 1)">$get_payload</span> = PhpJwt::verifyToken(<span style="color: rgba(128, 0, 128, 1)">$token</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">switch</span>(<span style="color: rgba(128, 0, 128, 1)">$get_payload</span>['code'<span style="color: rgba(0, 0, 0, 1)">]){
            </span><span style="color: rgba(0, 0, 255, 1)">case</span> 100:
                <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(-100, 'token验证失败'<span style="color: rgba(0, 0, 0, 1)">);
                </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">case</span> 200:
                <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(1000, 'token已过期'<span style="color: rgba(0, 0, 0, 1)">);</span>
<span style="color: rgba(0, 0, 0, 1)">      }
      </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;uid = <span style="color: rgba(128, 0, 128, 1)">$get_payload</span>['data']['uid'<span style="color: rgba(0, 0, 0, 1)">];
      </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;unionid = <span style="color: rgba(128, 0, 128, 1)">$get_payload</span>['data']['unionid'<span style="color: rgba(0, 0, 0, 1)">];
      </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;openid = <span style="color: rgba(128, 0, 128, 1)">$get_payload</span>['data']['openid'<span style="color: rgba(0, 0, 0, 1)">];
      </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;payload = <span style="color: rgba(128, 0, 128, 1)">$get_payload</span>['data'<span style="color: rgba(0, 0, 0, 1)">];
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * @name: 验证签名
   * @param {*}
   * @return {*}
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>   
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> verifySign()
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$token</span> = Request::<span style="color: rgba(0, 128, 128, 1)">header</span>()['authorization'<span style="color: rgba(0, 0, 0, 1)">];
      </span><span style="color: rgba(0, 0, 255, 1)">list</span>(<span style="color: rgba(128, 0, 128, 1)">$base64header</span>, <span style="color: rgba(128, 0, 128, 1)">$base64payload</span>, <span style="color: rgba(128, 0, 128, 1)">$jwtsign</span>) = <span style="color: rgba(0, 128, 128, 1)">explode</span>('.', <span style="color: rgba(128, 0, 128, 1)">$token</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(128, 0, 128, 1)">$params</span> = Request::<span style="color: rgba(0, 0, 0, 1)">post();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$params</span>['sign'<span style="color: rgba(0, 0, 0, 1)">])){
            </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(-100 ,'sign不能为空'<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$params</span>['timestamp'<span style="color: rgba(0, 0, 0, 1)">])){
            </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(-100, 'timestamp不能为空'<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">10分钟有效 毫秒级</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 128, 128, 1)">time</span>() * 1000 - <span style="color: rgba(128, 0, 128, 1)">$params</span>['timestamp'] &gt; 600000<span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> $this-&gt;apiResult(-100, '请求过期');</span>
<span style="color: rgba(0, 0, 0, 1)">      }
      </span><span style="color: rgba(128, 0, 128, 1)">$request_sign</span> = <span style="color: rgba(128, 0, 128, 1)">$params</span>['sign'<span style="color: rgba(0, 0, 0, 1)">];
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">对关联数组按照键名进行升序排序</span>
      <span style="color: rgba(0, 0, 255, 1)">unset</span>(<span style="color: rgba(128, 0, 128, 1)">$params</span>['sign'<span style="color: rgba(0, 0, 0, 1)">]);
      </span><span style="color: rgba(0, 128, 128, 1)">ksort</span>(<span style="color: rgba(128, 0, 128, 1)">$params</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(128, 0, 128, 1)">$param_str</span> = ''<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(128, 0, 128, 1)">$params</span> <span style="color: rgba(0, 0, 255, 1)">as</span> <span style="color: rgba(128, 0, 128, 1)">$k</span> =&gt; <span style="color: rgba(128, 0, 128, 1)">$v</span><span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(128, 0, 128, 1)">$v</span> = <span style="color: rgba(0, 128, 128, 1)">is_array</span>(<span style="color: rgba(128, 0, 128, 1)">$v</span>) ? json_encode(<span style="color: rgba(128, 0, 128, 1)">$v</span>, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE) : <span style="color: rgba(128, 0, 128, 1)">$v</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(128, 0, 128, 1)">$param_str</span> .= <span style="color: rgba(128, 0, 128, 1)">$k</span>.<span style="color: rgba(128, 0, 128, 1)">$v</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(128, 0, 128, 1)">$restr</span> = <span style="color: rgba(128, 0, 128, 1)">$param_str</span>.<span style="color: rgba(128, 0, 128, 1)">$jwtsign</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(128, 0, 128, 1)">$sign</span> = <span style="color: rgba(0, 128, 128, 1)">md5</span>(<span style="color: rgba(128, 0, 128, 1)">$restr</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 128, 128, 1)">strtolower</span>(<span style="color: rgba(128, 0, 128, 1)">$request_sign</span>) != <span style="color: rgba(0, 128, 128, 1)">strtolower</span>(<span style="color: rgba(128, 0, 128, 1)">$sign</span><span style="color: rgba(0, 0, 0, 1)">)) {
            </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(-100, '签名验证失败'<span style="color: rgba(0, 0, 0, 1)">);
      }
   
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span> apiResult(<span style="color: rgba(128, 0, 128, 1)">$code</span>, <span style="color: rgba(128, 0, 128, 1)">$msg</span>, <span style="color: rgba(128, 0, 128, 1)">$data</span> =<span style="color: rgba(0, 0, 0, 1)"> [])
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$result</span> =<span style="color: rgba(0, 0, 0, 1)"> [
            </span>'code' =&gt; <span style="color: rgba(128, 0, 128, 1)">$code</span>,
            'msg' =&gt; <span style="color: rgba(128, 0, 128, 1)">$msg</span>,
            'data' =&gt; <span style="color: rgba(128, 0, 128, 1)">$data</span>,<span style="color: rgba(0, 0, 0, 1)">
      ];
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span>(json_encode(<span style="color: rgba(128, 0, 128, 1)">$result</span>,<span style="color: rgba(0, 0, 0, 1)"> JSON_UNESCAPED_UNICODE));
    }
}</span></pre>
</div>
<p>    3、IndexBase.php</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* @Fun: 前台api基类
* @User: JessieK
* @Date: 2021-08-19 18:06:47
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
namespace app\controller;

</span><span style="color: rgba(0, 0, 255, 1)">use</span><span style="color: rgba(0, 0, 0, 1)"> app\model\Member;
</span><span style="color: rgba(0, 0, 255, 1)">use</span><span style="color: rgba(0, 0, 0, 1)"> think\facade\Request;
</span><span style="color: rgba(0, 0, 255, 1)">class</span> IndexBase <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> Base
{
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">会员信息</span>
    <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(128, 0, 128, 1)">$member_info</span><span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> __construct()
    {
      parent</span>::<span style="color: rgba(0, 0, 0, 1)">__construct();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">openid){
            </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(-100, '缺少参数', ['msg' =&gt; 'openid为空'<span style="color: rgba(0, 0, 0, 1)">]);
      }
      </span><span style="color: rgba(128, 0, 128, 1)">$memberModel</span> = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Member();
      </span><span style="color: rgba(128, 0, 128, 1)">$member_info</span> = <span style="color: rgba(128, 0, 128, 1)">$memberModel</span>-&gt;getUserInfoGather(<span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">openid);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(128, 0, 128, 1)">$member_info</span><span style="color: rgba(0, 0, 0, 1)">){
            </span><span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;apiResult(1001, '会员信息不存在'<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">锁粉操作</span>
      <span style="color: rgba(128, 0, 128, 1)">$from_uid</span> = Request::get('from_uid'<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$member_info</span>['from_uid']) &amp;&amp; !<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$from_uid</span><span style="color: rgba(0, 0, 0, 1)">)){
            </span><span style="color: rgba(128, 0, 128, 1)">$memberModel</span>-&gt;setFromUid(<span style="color: rgba(128, 0, 128, 1)">$member_info</span>['uid'], <span style="color: rgba(128, 0, 128, 1)">$from_uid</span>, Request::url(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">));
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> addlog('errorlog/request/', 'pro', '请求url='.json_encode(request()-&gt;get(), JSON_UNESCAPED_UNICODE));</span>
      <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;member_info = <span style="color: rgba(128, 0, 128, 1)">$member_info</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<ul>
<li>最后书写业务代码</li>
</ul>
<p>    1、对于需要验证token的,只需要继承IndexBase.php即可,基类里面直接对前端传过来的token进行验证是否合法。</p>
<p>    2、用户在登录后,获取到请求token,进行接口验证;请求token过期后,不用重新登录,用户用刷新token刷新,获取到新的请求token,既可以重新获取验证,拿到用户信息,避免频繁登录。</p>
<p>    3、上述代码,还附带verfySign签名,这个主要是可以配合token进行一起使用。jwt实现验证用户身份,签名实现接口请求是否合法。大致逻辑,在每次请求接口时带上签名和时间戳,具体签名逻辑可看上述代码。</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <br/>
-----END
<br/>
<br/>
影子是一个会撒谎的精灵,它在虚空中流浪和等待被发现之间;在存在与不存在之间....<br><br>
来源:https://www.cnblogs.com/camg/p/15407658.html
頁: [1]
查看完整版本: PHP代码篇(九)PHP接口开发如何使用JWT进行验证身份