直接上代码 先引入JWT核心文件
/** * Created by PhpStorm. * User: mybook-lhp * Date: 18/1/29 * Time: 下午2:10 */ namespace app\common\Service; use think\Exception; class JwtService { /** * 编码 * @param $payload * @param $key * @param string $algo * @return string * @throws Exception */ static public function encode($payload, $key, $algo = 'HS256') { $header = static::generateJwtHeader($payload, $algo); $segments = array( static::urlSafeB64Encode(json_encode($header)), static::urlSafeB64Encode(json_encode($payload)) ); $signing_input = implode('.', $segments); $signature = static::sign($signing_input, $key, $algo); $segments[] = static::urlsafeB64Encode($signature); return implode('.', $segments); } /** * 解码 * @param $jwt * @param null $key * @param bool $allowedAlgorithms * @return bool|mixed * @throws Exception */ static public function decode($jwt, $key = null, $allowedAlgorithms = true) { if (!strpos($jwt, '.')) { return false; } $tks = explode('.', $jwt); if (count($tks) != 3) { return false; } list($headb64, $payloadb64, $cryptob64) = $tks; if (null === ($header = json_decode(static::urlSafeB64Decode($headb64), true))) { return false; } if (null === $payload = json_decode(static::urlSafeB64Decode($payloadb64), true)) { return false; } $sig = static::urlSafeB64Decode($cryptob64); if ((bool)$allowedAlgorithms) { if (!isset($header['alg'])) { return false; } // check if bool arg supplied here to maintain BC if (is_array($allowedAlgorithms) && !in_array($header['alg'], $allowedAlgorithms)) { return false; } if (!static::verifySignature($sig, "$headb64.$payloadb64", $key, $header['alg'])) { return false; } } return $payload; } /** * @param $signature * @param $input * @param $key * @param string $algo * @return bool * @throws Exception */ static private function verifySignature($signature, $input, $key, $algo = 'HS256') { // use constants when possible, for HipHop support switch ($algo) { case'HS256': case'HS384': case'HS512': return static::hash_equals( static::sign($input, $key, $algo), $signature ); case 'RS256': return openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256') === 1; case 'RS384': return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384') === 1; case 'RS512': return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512') === 1; default: throw new Exception("Unsupported or invalid signing algorithm."); } } /** * @param $input * @param $key * @param string $algo * @return string * @throws Exception */ static private function sign($input, $key, $algo = 'HS256') { switch ($algo) { case 'HS256': return hash_hmac('sha256', $input, $key, true); case 'HS384': return hash_hmac('sha384', $input, $key, true); case 'HS512': return hash_hmac('sha512', $input, $key, true); case 'RS256': return static::generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256'); case 'RS384': return static::generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384'); case 'RS512': return static::generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512'); default: throw new Exception("Unsupported or invalid signing algorithm."); } } /** * @param $input * @param $key * @param string $algo * @return mixed * @throws Exception */ static private function generateRSASignature($input, $key, $algo) { if (!openssl_sign($input, $signature, $key, $algo)) { throw new Exception("Unable to sign data."); } return $signature; } /** * @param string $data * @return string */ static public function urlSafeB64Encode($data) { $b64 = base64_encode($data); $b64 = str_replace(array('+', '/', "\r", "\n", '='), array('-', '_'), $b64); return $b64; } /** * @param string $b64 * @return mixed|string */ static public function urlSafeB64Decode($b64) { $b64 = str_replace(array('-', '_'), array('+', '/'), $b64); return base64_decode($b64); } /** * Override to create a custom header */ static protected function generateJwtHeader($payload, $algorithm) { return [ 'typ' => 'JWT', 'alg' => $algorithm, ]; } /** * @param string $a * @param string $b * @return bool */ static protected function hash_equals($a, $b) { if (function_exists('hash_equals')) { return hash_equals($a, $b); } $diff = strlen($a) ^ strlen($b); for ($i = 0; $i < strlen($a) && $i < strlen($b); $i ++) { $diff |= ord($a[$i]) ^ ord($b[$i]); } return $diff === 0; } }第二步 登陆控制器
这里主要是获取登陆用户名和密码 然后账号密码验证通过后生成用户唯一标示 (姑且ji OpenID ),其中$model中放置一些非敏感信息。
namespace app\admin\controller; use app\common\model\UserModel; use app\common\Service\JwtService; use app\common\utils\UtilEncryption; use app\common\utils\UtilFilter; use app\common\utils\UtilStateCode; use think\Controller; class Login extends Controller { public function token() { $parm = $this->request->param(); $lname = isset($parm['login_name']) ? trim($parm['login_name']) : ""; $lname = UtilFilter::addslashesStr($lname); $enter_pwd = isset($parm['pwd']) ? trim($parm['pwd']) : ""; $pwd = UtilEncryption::encryptMD5($enter_pwd, config('auth.auto_key')); $model = UserModel::getUser(['username' => $lname, 'password' => $pwd]); if ($model) { unset($model['password']); $token = JwtService::encode($model, config('auth.auto_key')); return json(['token' => $token]); } else { $this->error('请求数据错误', UtilStateCode::ALL_ERROR); } } }第三步 请求访问验证
其中为http请求头里面回传回来的OpenID
HTTP_X_AUTH_TOKEN
$token = $_SERVER['HTTP_X_AUTH_TOKEN']; if (!empty($token)) { $users = JwtService::decode($token, config('auth.auto_key')); if ($users) { $loginflag = true; $this->user = $users; } }
namespace app\common\controller; use app\common\Service\AuthServer; use app\common\Service\JwtService; use app\common\utils\UtilStateCode; use think\Controller; class BaseController extends Controller { protected $user = null; public $map = null; public $put = null; public $put_id = null; public function __construct() { parent::__construct(); $this->map(); $this->put(); config('app.default_return_type', 'json'); } public function initialize() { parent::initialize(); // TODO: Change the autogenerated stub $loginflag = false; $accessflag = true; $token = $this->request->header('X-Auth-Token', null); if ($token === null) { $this->error('Token不存在,你没有权限访问!'); } $token = $_SERVER['HTTP_X_AUTH_TOKEN']; if (!empty($token)) { $users = JwtService::decode($token, config('auth.auto_key')); if ($users) { $loginflag = true; $this->user = $users; } } $action = $this->request->action(); $controller = $this->request->controller(); $module = $this->request->module(); $URL = $module . '/' . $controller . '/' . $action; if (!static::authcheck($URL, $users['id'])) { return json($users['username'] . '很抱歉,此项操作您没有权限!'); } if ($loginflag && $accessflag) { return true; } else { if (!$loginflag) { $this->error("未登录", UtilStateCode::LOGIN_REQUIRE); } else { $this->error("没有权限", UtilStateCode::ACCESS_ERROR); } } } protected function map() { $this->map['id'] = $this->request->param('id', false); $this->map['page'] = $this->request->param('page', '1'); $this->map['rows'] = $this->request->param('rows', '20'); $this->map['order'] = [$this->request->param('sort', 'id') => $this->request->param('order', 'desc')]; return $this; } protected function put() { $this->put_id = $this->request->get('id'); $this->put = $this->request->put(); return $this; } /** * 检查权限 * @param name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组 * @param uid int 认证用户的id * @param string mode 执行check的模式 * @param relation string 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证 * @return boolean 通过验证返回true;失败返回false */ static protected function authcheck($name, $uid, $type = 1, $mode = 'url', $relation = 'or') { if (!in_array($uid, config('auth.ADMINISTRATOR'))) { $auth = new AuthServer(); return $auth->check($name, $uid, $type, $mode, $relation) ? true : false; } else { return true; } } }以上是PHP的实现
下面是Easyui的实现
js登陆代码
var loginFun = function () { if ($('#loginform').form('validate')) { Qrck.post(Qrck.baseApiLoginUrl, $('#loginform').serialize(), function () { $.messager.progress({ text: '登录验证中请稍后……', }); }, function (data) { var login_name = $('#username').val(); $.cookie('login_name', login_name, {path: Qrck.cookiePath}); $.cookie('token', data.token, {path: Qrck.cookiePath}); window.location.href = Qrck.baseHtmlUrl + Qrck.baseHtmlMain; }, function (data) { $.messager.progress('close'); $.messager.show({ title: '提示', msg: '用户不存在或密码错误,请重新登录', showType: 'slide', }); }); } };用户登录后会得到OpenID 也就是上面的token ,然后将Token存入cookie中
GetAjaxData: function (url, method, parm, beforeSend, success, error) { var dataurl = this.baseApiUrl + url; $.ajax({ headers: { "X-Auth-Token": $.cookie('token') }, type: method, url: dataurl, cache: false, async: this.asyncFlag, dataType: this.dataType, data: parm, beforeSend: function () { if (typeof beforeSend === "function") { beforeSend(); } }, success: function (result) { if (typeof success === "function") { success(result); } }, error: function (result) { if (typeof error === "function") { error(result); } }, }); },在每一次请求的ajax请求时,都将OpenID放入http请求头里面,这样就不用单独传参。
JWT 类可以支持OpenSSL证书的加密更安全