uni-app结合laravel实现免登陆

最近发现一个挺好玩的东西,免登陆积分商城,仔细研究分析后得出结论,无论是商城还是其他,免登录都可以玩玩的。原理也很简单,浏览器都有指纹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);
    }
}

三、总结

加密的时候可以自行穿插一些其他信息来验证是否为合法请求,具体基本上每行代码都写了注释,供大家参考。

相关推荐
Pedantic13 分钟前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘29 分钟前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆39 分钟前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师2 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆2 小时前
VSCode自动格式化三要素
前端
爱勇宝2 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen3 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518135 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode5 小时前
Redis 在生产项目的使用
前端·后端