php 接口安全解决方案,php接口数据安全解决方案(一)

前言

目的:

1.实现前后端代码分离,分布式部署

2.利用token替代session实现状态保持,token是有时效性的满足退出登录,token存入redis可以解决不同服务器之间session不同步的问题,满足分布式部署

3.利用sign,前端按照约定的方式组合加密生成字符串来校验用户传递的参数跟后端接收的参数是否一直,保障接口数据传递的安全

4.利用nonce,timestamp来保障每次请求的生成sign不一致,并将sign与nonce组合存入redis,来防止api接口重放

目录介绍

├── Core

│   ├── Common.php(常用的公用方法)

│   ├── Controller.php (控制器基类)

│   └── RedisService.php (redis操作类)

├── config.php (redis以及是否开启关闭接口校验的配置项)

├── login.php (登录获取token入口)

└── user.php(获取用户信息,执行整个接口校验流程)

登录鉴权图

d6cf9278833613a3b94ceb2af950da8b.png

接口请求安全性校验整体流程图

2da5c39af1341372bf3cef31b87f383e.png

代码展示

common.php

namespace Core;

/**

* @desc 公用方法

* Class Common

*/

class Common{

/**

* @desc 输出json数据

* @param $data

*/

public static function outJson($code,$msg,$data=null){

$outData = [

'code'=>$code,

'msg'=>$msg,

];

if(!empty($data)){

$outData['data'] = $data;

}

echo json_encode($outData);

die();

}

/***

* @desc 创建token

* @param $uid

*/

public static function createToken($uid){

$time = time();

$rand = mt_rand(100,999);

$token = md5($time.$rand.'jwt-token'.$uid);

return $token;

}

/**

* @desc 获取配置信息

* @param $type 配置信息的类型,为空获取所有配置信息

*/

public static function getConfig($type=''){

$config = include "./config.php";

if(empty($type)){

return $config;

}else{

if(isset($config[$type])){

return $config[$type];

}

return [];

}

}

}

RedisService.php

namespace Core;

/*

*@desc redis类操作文件

**/

class RedisService{

private $redis;

protected $host;

protected $port;

protected $auth;

protected $dbId=0;

static private $_instance;

public $error;

/*

*@desc 私有化构造函数防止直接实例化

**/

private function __construct($config){

$this->redis = new \Redis();

$this->port = $config['port'] ? $config['port'] : 6379;

$this->host = $config['host'];

if(isset($config['db_id'])){

$this->dbId = $config['db_id'];

$this->redis->connect($this->host, $this->port);

}

if(isset($config['auth']))

{

$this->redis->auth($config['auth']);

$this->auth = $config['auth'];

}

$this->redis->select($this->dbId);

}

/**

*@desc 得到实例化的对象

***/

public static function getInstance($config){

if(!self::$_instance instanceof self) {

self::$_instance = new self($config);

}

return self::$_instance;

}

/**

*@desc 防止克隆

**/

private function __clone(){}

/*

*@desc 设置字符串类型的值,以及失效时间

**/

public function set($key,$value=0,$timeout=0){

if(empty($value)){

$this->error = "设置键值不能够为空哦~";

return $this->error;

}

$res = $this->redis->set($key,$value);

if($timeout){

$this->redis->expire($key,$timeout);

}

return $res;

}

/**

*@desc 获取字符串类型的值

**/

public function get($key){

return $this->redis->get($key);

}

}

Controller.php

namespace Core;

use Core\Common;

use Core\RedisService;

/***

* @desc 控制器基类

* Class Controller

* @package Core

*/

class Controller{

//接口中的token

public $token;

public $mid;

public $redis;

public $_config;

public $sign;

public $nonce;

/**

* @desc 初始化处理

* 1.获取配置文件

* 2.获取redis对象

* 3.token校验

* 4.校验api的合法性check_api为true校验,为false不用校验

* 5.sign签名验证

* 6.校验nonce,预防接口重放

*/

public function __construct()

{

//1.获取配置文件

$this->_config = Common::getConfig();

//2.获取redis对象

$redisConfig = $this->_config['redis'];

$this->redis = RedisService::getInstance($redisConfig);

//3.token校验

$this->checkToken();

//4.校验api的合法性check_api为true校验,为false不用校验

if($this->_config['checkApi']){

// 5. sign签名验证

$this->checkSign();

//6.校验nonce,预防接口重放

$this->checkNonce();

}

}

/**

* @desc 校验token的有效性

*/

private function checkToken(){

if(!isset($_POST['token'])){

Common::outJson('10000','token不能够为空');

}

$this->token = $_POST['token'];

$key = "token:".$this->token;

$mid = $this->redis->get($key);

if(!$mid){

Common::outJson('10001','token已过期或不合法,请先登录系统 ');

}

$this->mid = $mid;

}

/**

* @desc 校验签名

*/

private function checkSign(){

if(!isset($_GET['sign'])){

Common::outJson('10002','sign校验码为空');

}

$this->sign = $_GET['sign'];

$postParams = $_POST;

$params = [];

foreach($postParams as $k=>$v) {

$params[] = sprintf("%s%s", $k,$v);

}

sort($params);

$apiSerect = $this->_config['apiSerect'];

$str = sprintf("%s%s%s", $apiSerect, implode('', $params), $apiSerect);

if ( md5($str) != $this->sign ) {

Common::outJson('10004','传递的数据被篡改,请求不合法');

}

}

/**

* @desc nonce校验预防接口重放

*/

private function checkNonce(){

if(!isset($_POST['nonce'])){

Common::outJson('10003','nonce为空');

}

$this->nonce = $_POST['nonce'];

$nonceKey = sprintf("sign:%s:nonce:%s", $this->sign, $this->nonce);

$nonV = $this->redis->get($nonceKey);

if ( !empty($nonV)) {

Common::outJson('10005','该url已经被调用过,不能够重复使用');

} else {

$this->redis->set($nonceKey,$this->nonce,360);

}

}

}

config.php

return [

//redis的配置

'redis' => [

'host' => 'localhost',

'port' => '6379',

'auth' => '123456',

'db_id' => 0,//redis的第几个数据库仓库

],

//是否开启接口校验,true开启,false,关闭

'checkApi'=>true,

//加密sign的盐值

'apiSerect'=>'test_jwt'

];

login.php

/**

* @desc 自动加载类库

*/

spl_autoload_register(function($className){

$arr = explode('\\',$className);

include $arr[0].'/'.$arr[1].'.php';

});

use Core\Common;

use Core\RedisService;

if(!isset($_POST['username']) || !isset($_POST['pwd']) ){

Common::outJson(-1,'请输入用户名和密码');

}

$username = $_POST['username'];

$pwd = $_POST['pwd'];

if($username!='admin' || $pwd!='123456' ){

Common::outJson(-1,'用户名或密码错误');

}

//创建token并存入redis,token对应的值为用户的id

$config = Common::getConfig('redis');

$redis = RedisService::getInstance($config);

//假设用户id为2

$uid = 2;

$token = Common::createToken($uid);

$key = "token:".$token;

$redis->set($key,$uid,3600);

$data['token'] = $token;

Common::outJson(0,'登录成功',$data);

user.php

/**

* @desc 自动加载类库

*/

spl_autoload_register(function($className){

$arr = explode('\\',$className);

include $arr[0].'/'.$arr[1].'.php';

});

use Core\Controller;

use Core\Common;

class UserController extends Controller{

/***

* @desc 获取用户信息

*/

public function getUser(){

$userInfo = [

"id"=>2,

"name"=>'巴八灵',

"age"=>30,

];

if($this->mid==$_POST['mid']){

Common::outJson(0,'成功获取用户信息',$userInfo);

}else{

Common::outJson(-1,'未找到该用户信息');

}

}

}

//获取用户信息

$user = new UserController();

$user->getUser();

演示用户登录

简要描述:

用户登录接口

请求URL:

http://localhost/login.php

请求方式:

POST

参数:

参数名

必选

类型

说明

username

string

用户名

pwd

string

密码

返回示例

{

"code": 0,

"msg": "登录成功",

"data": {

"token": "86b58ada26a20a323f390dd5a92aec2a"

}

}

{

"code": -1,

"msg": "用户名或密码错误"

}

演示获取用户信息

简要描述:

获取用户信息,校验整个接口安全的流程

请求URL:

http://localhost/user.php?sign=f39b0f2dea817dd9dbef9e6a2bf478de

请求方式:

POST

参数:

参数名

必选

类型

说明

token

string

token

mid

int

用户id

nonce

string

防止用户重放字符串 md5加密串

timestamp

int

当前时间戳

返回示例

{

"code": 0,

"msg": "成功获取用户信息",

"data": {

"id": 2,

"name": "巴八灵",

"age": 30

}

}

{

"code": "10005",

"msg": "该url已经被调用过,不能够重复使用"

}

{

"code": "10004",

"msg": "传递的数据被篡改,请求不合法"

}

{

"code": -1,

"msg": "未找到该用户信息"

}

文章完整代码地址

后记

上面完整的实现了整个api的安全过程,包括接口token生成时效性合法性验证,接口数据传输防篡改,接口防重放实现。仅仅靠这还不能够最大限制保证接口的安全。条件满足的情况下可以使用https协议从数据底层来提高安全性,另外本实现过程token是使用redis存储,下一篇文章我们将使用第三方开发的库实现JWT的规范操作,来替代redis的使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537922.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Teamview连接Windows server问题

场景: 服务器在集团总部杭州,网管在集团宁波分公司,连接服务器通过内网远程桌面。过程: 网管给了tv的账号,密码。连接的时候一直连不上去。卡在“正在初始化连接参数”。后来网管不信,远程桌面了下&#xf…

nginx An attempt was made to access a socket in a way forbidden by its access permissions

在安装了 sqlserver2008 的win7 与 win2008 上启动 nginx,绑定80端口,报错: nginx An attempt was made to access a socket in a way forbidden by its access permissions查了百度,说修改注册表,但我的电脑上找不到文…

php的cms是什么意思,phpcms是什么系统

什么是phpcms?Phpcms 是国内领先的网站内容管理系统,同时也是一个开源的PHP开发框架。Phpcms由内容模型、会员、问吧、专题、财务、订单、广告、邮件订阅、 短消息、自定义表单、全站搜索等20多个功能模块组成,内置新闻、图片、下载、信息、产…

【python】 time模块和datetime模块详解 【转】

一、time模块 time模块中时间表现的格式主要有三种: a、timestamp时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量 b、struct_time时间元组,共有九个元素组。 c、format time 格式化时间,已格式化的结构使时间更…

spring boot Exception in Thread “main” java.lang.classNoFoundException

在客户测试环境部署,通过打包成jar,使用命令 nohup java -jar /usr/local/tomcat/shirencai/ct-peixun-provider.jar –spring.profiles.activestage > /usr/local/tomcat/shirencai/ct-peixun-provider-temp.txt & 报错后来排查以为是内存不够。…

php源码自动识别文本中的链接,自动加载识别文件Auto.php

用于本应用的控制器自动加载类设置&#xff0c;用法如同\CodeIgniter\Config\AutoloadConfig自动加载识别文件:dayrui/App/应用目录/Config/Auto.php语法格式&#xff1a;<?php // 自动加载识别文件return [/*** 命名空间映射关系*/psr4 > [],/*** 类名映射关系*/classm…

防抓包重放php,超简单最基本的WEB抓包改包重放的方法

【注意&#xff1a;此文章为博主原创文章&#xff01;转载需注意&#xff0c;请带原文链接&#xff0c;至少也要是txt格式&#xff01;】很多很多刚刚接触的同事问我如何抓包&#xff0c;如果讲用工具可能还涉及什么装证书&#xff0c;熟悉使用工具等等&#xff0c;特别繁琐&am…

mysql查询很慢优化方法1

解决方法&#xff1a; 关联的字段建索引。 具体分析如下&#xff1a;举例&#xff1a; 表格&#xff1a;培训学生表&#xff0c;班级报名表 需求&#xff1a;查询出学生报了哪些班级 两表有个关联字段“CD”&#xff08;学生学号&#xff09;。 视图sql&#xff1a; SELECTt_px…

php找出函数定义位置,WordPress如何快速定位PHP函数所在文件位置及代码行号?

有时候我们需要修改别人源码里的代码&#xff0c;却找不到对应的函数放在了哪儿&#xff0c;就可以用使用本文介绍的办法&#xff0c;帮你快速定位函数位置。特别是某些写法不规范的WordPress主题&#xff0c;各种模块&#xff0c;函数到处放&#xff0c;找半天的那种。那么Wor…

Phoenix:全局索引设计实践

概述 全局索引是Phoenix的重要特性&#xff0c;合理的使用二级索引能降低查询延时&#xff0c;让集群资源得以充分利用。 本文将讲述如何高效的设计和使用索引。 全局索引说明 全局索引的根本是通过单独的HBase表来存储数据表的索引数据。我们通过如下示例看索引数据和主表数据…

php 美颜,怀念以前无滤镜美颜的影视剧

滤镜是为了照片质量更高一些&#xff0c;色彩更真实突出的一种补助工具。自从有了美颜和滤镜后&#xff0c;大家的生活都变成了彩色。开了滤镜美颜&#xff0c;小伙伴们有木有感觉生活水平变高了&#xff1f;但影视剧&#xff0c;好像变成了单色&#xff1f;&#xff01;(注意&…

代码质量管理工具】——sonar

原文地址&#xff1a;https://blog.csdn.net/luckystar689/article/details/53871821 ------------------------------------------------------------------------ 【前言】 bug越改越多&#xff0c;程序一换数据就崩&#xff0c;这就是目前我们系统的一个现状。在这之前&am…

马凯军201771010116《面向对象程序设计(java)》第二周学习总结

第一部分&#xff1a;理论知识学习部分 &#xff08;1&#xff09;基本知识&#xff1a;简单应用程序的结构&#xff1b;Java环境里的注释方式&#xff1b; &#xff08;2&#xff09;数据类型&#xff08;4种整型、2种浮点型、1种字符型‘char’、真值型‘Boolean’。 &#x…

Linux集群架构(LVS DR模式搭建、keepalived + LVS)

为什么80%的码农都做不了架构师&#xff1f;>>> LVS DR模式搭建 准备工作&#xff1a;三台机器 分发器&#xff0c;也叫调度器&#xff08;简写为dir&#xff09;&#xff1a;192.168.248.128 rs1 &#xff1a;192.168.248.129 rs2 : 192.168.248.130 vip : 192.16…

Windows下SVN回滚到旧版本(TortoiseSVN)

原文地址&#xff1a;https://www.cnblogs.com/tommy-huang/p/4729634.html ---------------------------------------- 当发现新提交的代码有问题&#xff0c;然后想将某个旧的版本作为最新的版本时&#xff0c;可以使用回滚&#xff0c; 操作步骤如下&#xff1a; 1. 签出…

看懂架构设计中的服务隔离

前言 我们在做系统架构设计的时候&#xff0c;经常离不开的一个话题就是进行服务的隔离设计。 那什么是「服务隔离」呢&#xff1f; 顾名思义&#xff0c;它是指将系统按照一定的原则划分为若干个服务模块&#xff0c;各个模块之间相对独立&#xff0c;无强依赖。当有故障发生时…

[五] JavaIO之InputStream OutputStream简介 方法列表说明

InputStream 和 OutputStream 对于字节流的输入和输出 是作为协议的存在 所以有必要了解下这两个类提供出来的基本约定 这两个类是抽象类,而且基本上没什么实现,都是依赖于子类具体的去实现 但是他是对于其子类 协议纲领一般的存在 了解清楚每一个方法含义,对于后续具体的子类将…

[转]资本经营董事长班告诉你:不只企业有商业模式,个人商业价值更重要

本文转自&#xff1a;http://blog.sina.com.cn/s/blog_181b5b47e0102xz4v.html 东方财智-资本经营董事长班>>> 经营者如何轻松驾驭金融工具为企业发展加速、升级互联网工具实现品牌营销裂变、利用管理工具优化公司内部结构&#xff0c;把握前沿视角&#xff0c;实现快…

CPR认证-建材CE认证-305/2011/EU

建材CPR认证 - Regulation (EU) No 305/2011  2011年3月&#xff0c;欧盟颁布了新建筑产品法规RETULATION &#xff08;EU&#xff09; No 305/2011 – CPR&#xff0c;并通告了新法规于2013年7月进入强制执行&#xff0c;取代了老CPD指令89/106/EEC。  新的建材CPR认证也是…

python支持复数类型以下什么说法是错误的,【Python】专项练习题(1)

1.python代码如下:foo [1,2]foo1 foofoo.append(3)A.foo 值为[1,2]B.foo 值为[1,2,3]C.foo1 值为[1,2]D.foo1 值为[1,2,3]答案&#xff1a;B D链接&#xff1a;https://www.nowcoder.com/profile/153165401/myFollowings/detail/20055659来源&#xff1a;牛客网2.下列程序打印…