最近发现一个挺好玩的东西,免登陆积分商城,仔细研究分析后得出结论,无论是商城还是其他,免登录都可以玩玩的。原理也很简单,浏览器都有指纹ID,APP有设备唯一标识,最终选择使用uni-app与laravel写了个免登陆系统。
一、uni-app
1.安装Fingerprint2
npm install --save fingerprintjs2
2.安装CryptoJS
npm install --save crypto-js
3.完整的全局请求封装代码
js
const base_url = 'https://xxx.com/api/'
import Fingerprint2 from 'fingerprintjs2'
import CryptoJS from 'crypto-js'
const encrypt = (str) => {
//密钥16位
var key = CryptoJS.enc.Utf8.parse('Uy2LlvFGFGbgIH8a');
//加密向量16位
var iv = CryptoJS.enc.Utf8.parse('YdRrSPUrVlQ1UD4W');
var encrypted = CryptoJS.AES.encrypt(str, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
export default (url, params = {}) => {
// 获取存储的设备标识
let device = uni.getStorageSync('device')
// 获取存储的客户端类型
let client = uni.getStorageSync('client')
// 获取设备信息
let app = uni.getSystemInfoSync()
// #ifdef APP
// 判断设备标识是否不在
if (!device) {
// 存储设备标识
uni.setStorageSync('device', app.deviceId)
// 判断是否为iOS
if (app.platform == 'ios') {
uni.setStorageSync('client', 'iOS')
}
// 判断是否为Android
if (app.platform == 'android') {
uni.setStorageSync('client', 'Android')
}
}
// #endif
// #ifndef APP
// 获取浏览器请求头
const userAgent = navigator.userAgent.toLowerCase();
Fingerprint2.get(function(components) {
const values = components.map(function(component, index) {
if (index === 0) {
//把微信浏览器里UA的wifi或4G等网络替换成空,不然切换网络会ID不一样
return component.value.replace(/\bNetType\/\w+\b/, '')
}
return component.value
})
// 存储唯一标识
uni.setStorageSync('device', Fingerprint2.x64hash128(values.join(''), 31))
});
// 判断是否为手机浏览器
if (/ipad|iphone|midp|rv:1.2.3.4|ucweb|android|windows ce|windows mobile/.test(userAgent)) {
uni.setStorageSync('client', 'wap')
} else {
uni.setStorageSync('client', 'web')
}
// 重新获取设备标识
device = uni.getStorageSync('device')
// 重新获取客户端类型
client = uni.getStorageSync('client')
// #endif
// 加密设备信息提交到后台
params.device = encrypt(JSON.stringify({
// 设备唯一标识
device: device,
// 应用的AppID
appId: app.appId,
// 应用的APP名称
appName: app.appName,
// 获取当前时间戳
time: Math.round(new Date().getTime() / 1000).toString()
}));
return new Promise((resolve, reject) => {
uni.request({
url: base_url + url,
method: 'POST',
data: params,
success(response) {
const res = response.data
// 判断请求是否成功
if (res.code == 200) {
resolve(res);
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
},
fail(err) {
reject(err);
},
complete() {}
});
}).catch((e) => {});
};
二、laravel
检测登录中间件完整代码
php
<?php
namespace App\Http\Middleware;
use App\Http\Controllers\Controller;
// 引入用户模型
use App\Models\Member;
use Carbon\Carbon;
use Closure;
use Illuminate\Http\Request;
class CheckUser
{
public function handle(Request $request, Closure $next)
{
// 获取登录的用户信息
$member = auth('member')->user();
// 如果没有登录的用户信息
if (!$member) {
// 判断是否传入设备信息
if ($request->device) {
// 解密设备编码
$device = json_decode($this->decryptString($request->device), true);
// 获取当前时间戳
$datetime1 = Carbon::createFromTimestamp(time());
// 获取传过来的时间戳
$datetime2 = Carbon::createFromTimestamp($device['time']);
// 使用diffInSeconds方法获取两个时间的时间差(以秒为单位)
$timeDifference = $datetime1->diffInSeconds($datetime2);
// 判断设备解密是否正确以及时间差小于10秒
if (!isset($device['appId']) || !isset($device['device']) || !isset($device['appName']) || abs($timeDifference) >= 10) {
return response()->json(['code' => 0, 'msg' => '别想太多,老老实实使用']);
}
if ($device['appId'] != '__UNI__A88888888' || $device['appName'] != 'APP名称') {
return response()->json(['code' => 0, 'msg' => '别想太多,老老实实使用']);
}
// 获取设备的用户信息
$member = Member::where('device', $device['device'])->orWhere('phone', $request->phone)->first();
// 判断用户是否不存在
if (!$member) {
// 创建一个新的用户
$member = Member::create([
// 生成随机UID
'uid' => mt_rand(111111, 999999),
// 获取用户手机号
'phone' => $request->phone,
// 获取上级ID
'parent' => $request->parent,
// 获取设备唯一标识
'device' => $device['device']
]);
} else {
// 判断用户的设备不等于请求的设备
if ($member['device'] != $device['device']) {
// 更新设备信息
$member = $member->update(['device' => $device['device']]);
}
}
}
}
if (isset($member['uid'])) {
$request->headers->set('uid', $member['uid']);
}
return $next($request);
}
// 解密函数
public function decryptString($str)
{
// 设置密钥
$key = 'Uy2LlvFGFGbgIH8a';
// 设置偏移量
$iv = 'YdRrSPUrVlQ1UD4W';
// 返回解密后的字符串
return openssl_decrypt(base64_decode($str), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
}
}
三、总结
加密的时候可以自行穿插一些其他信息来验证是否为合法请求,具体基本上每行代码都写了注释,供大家参考。