登录功能与及用token对uid的加密和管理员对数据的
文件的展示:1.前台路由文件:index.js
2.前台登录文件:Login.vue
import { createRouter, createWebHistory } from 'vue-router'
import Ad from '../views/kedaishu/ad.vue'
import Index from '../views/Index.vue'
import Login from '../views/Login.vue'
import Course from '../views/kedaishu/course.vue'
import AdminUser from '../views/admin/user.vue'
const routes = [
{
path: '/', // 路由地址配置,就是浏览器上的url
name: 'home', // 是页面的名称,自定义,但是不要重复
component: Index, // 这个是页面文件的路径
},
{
path: '/login',//创建登录页面的路由
name: 'login',
component: Login,
},
{
path: '/admin_user', // 路由地址配置,就是浏览器上的url
name: 'AdminUser', // 是页面的名称,自定义,但是不要重复
component: AdminUser, // 这个是页面文件的路径
},
{
path: '/ad', // 路由地址配置,就是浏览器上的url
name: 'ad', // 是页面的名称,自定义,但是不要重复
component: Ad, // 这个是页面文件的路径
},
{
path: '/course', // 路由地址配置,就是浏览器上的url
name: 'course', // 是页面的名称,自定义,但是不要重复
component: Course, // 这个是页面文件的路径
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
3.前台用户文件:user.vue
<template>
<div class="container">
<div style="text-align: center">
<img src="@/assets/logo.png" alt="logo" />
<h3>phpAdmin后台管理</h3>
</div>
<div class="main">
<!-- label-width="auto" 导致ElementPlusError: [ElForm] unexpected width 0 -->
<el-form :model="state" size="large">
<el-form-item prop="account">
<el-input v-model="state.account" name="account" class="w-50 m-2" placeholder="请输入账号">
<template #prefix>
<el-icon class="el-input__icon" style="color: #1890ff"><Avatar /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="state.password" name="password" type="password" class="w-50 m-2" placeholder="请输入密码" show-password>
<template #prefix>
<el-icon class="el-input__icon" style="color: #1890ff"><Lock /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="remember">
<el-checkbox v-model="state.remember" label="1" size="large">自动登录</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 100%" @click="onSubmit()">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import {reactive} from 'vue';
import { useRouter } from 'vue-router';
import {Login} from '../network/index.js';
import { ElMessage } from 'element-plus'
//这是向服务器请求时所输入的数据,创建登录时的state,与本文11,18,25行相对应
const state = reactive({
account : "",
password : "",
remember : true
});
//定义路由器
const router = useRouter();
//从浏览器存储里获取用户登录时的数据;uid
//const uid=window.localStorage.getItem("uid");
const ticket=window.localStorage.getItem("ticket");
//如果有票据就跳转到首页
if(ticket){
ElMessage({ //增加这一条就不会跳转到登录页面了
message:'您已登录',
type:'success'
});
router.push("/");
}
//提交,onSubmit与28行相对应
const onSubmit=()=>{
console.log(state);//先打印一下数据
Login(state).then((e)=>{ //state是传值,e是返回值
console.log(e);
if(e.code==0){ //如果e的值为0时,则登录成功
ElMessage({
message:e.msg,
type:'success'
});
//把登录时的数据保存到浏览器的存储中
//window.localStorage.setItem('uid',e.data.uid);
window.localStorage.setItem('ticket',e.data.ticket);
window.localStorage.setItem('name',e.data.name);
//路由器跳转到首页
router.push("/");
}else{ //反之登录不成功
ElMessage({
message:e.msg,
type:'error'
});
}
})
}
// const router = useRouter();
// const ticket = window.localStorage.getItem("ticket");
// if(ticket){
// ElMessage({
// message: '您已登录',
// type: 'success'
// });
// router.push("/");
// }
// const onSubmit = ()=>{
// Login(state).then( (e)=>{
// console.log(e.data);
// if(e.code == 0){
// ElMessage({
// message: e.msg,
// type: 'success'
// });
// 登录完成以后
// window.localStorage.setItem("ticket",e.data.ticket);
// window.localStorage.setItem("name",e.data.name);
// router.push("/");
// }else{
// ElMessage({
// message: e.msg,
// type: 'error'
// });
// }
// } )
// }
</script>
<style>
.container {
position: relative;
width: 100%;
min-height: 100%;
padding: 110px 0 144px;
background-repeat: no-repeat;
background-position: center 110px;
background-size: 100%;
}
.main {
width: 368px;
min-width: 260px;
margin: 50px auto;
}
.el-icon {
color: #359eff;
}
</style>
4.前台的index文件:index.js
<template>
<div style="margin:0 0 20px 0;">
<!-- 当添加按钮点击时调用add方法并传一个为0的值 -->
<el-button type="success" size="large" @click="add(0)">添加</el-button>
</div>
<el-form label-width="120px" :inline="true">
<el-form-item label="搜索项">
<el-select placeholder="请选择您要搜索的项">
<el-option label="姓名" value="" />
<el-option label="手机" value="" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary">搜索</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData.lists" border style="width: 100%" size="large">
<el-table-column prop="uid" label="ID" />
<el-table-column prop="account" label="账号" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="phone" label="手机号" />
<el-table-column prop="status" label="状态" />
<el-table-column prop="add_time" label="入职时间" />
<el-table-column prop="last_time" label="最后登录时间" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" type="primary" @click="add(scope.row)">修改</el-button>
<!-- <el-button size="small" type="danger">删除</el-button> -->
<el-popconfirm title="确认删除吗?" @confirm="del(scope.row)">
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- 弹出的对话框(弹窗) -->
<el-dialog v-model="data.is_form" :title="data.title" width="30%">
<!-- 弹窗中的表单 -->
<el-form :model="fromData" label-width="120px" size="small">
<el-form-item label="账号">
<el-input v-model="fromData.account"/>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="fromData.password" type="password" show-password :placeholder="data.plac"/>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="fromData.name"/>
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="fromData.phone"/>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="fromData.status" placeholder="请选择状态">
<el-option label="开启" :value="1" />
<el-option label="关闭" :value="0" />
</el-select>
</el-form-item>
<el-form-item label="入职时间">
<el-date-picker
v-model="fromData.add_time"
type="date"
placeholder="选一日期"
:size="size"
/>
</el-form-item>
<el-form-item label="最后登录时间">
<el-date-picker
v-model="fromData.last_time"
type="date"
placeholder="选一日期"
:size="size"
/>
</el-form-item>
<!-- '确定'按钮 -->
<el-button type="success" size="largr" @click="fun">确定</el-button>
</el-form>
</el-dialog>
</template>
<script setup>
//从vue中引入响应式数据reactive(数据改变后视图也会自动更新,就叫响应式数据)
import {reactive} from 'vue';
import axios from "axios";
//引入index.js文件中的UserLists,UserSave方法
import {UserLists,UserSave,UserDel} from "../../network/index.js"
// 引入element组件中的消息框
import { ElMessage } from 'element-plus';
// 引入element组件中的告示框
import { alertProps } from 'element-plus';
//运用票据(ticket),还要引入下面路由
import { useRouter } from 'vue-router';
//获取票据(ticket)
const ticket=window.localStorage.getItem('ticket');
const router=useRouter();
//增加下面这一条就不能随便打开用户文件了,要先登录才行
if(!ticket){
ElMessage({
message:'请先登录',
type:'error'
});
router.push("/login");
}
// reactive表示data下有多个值,如data.is_form,data.title
const data=reactive({
is_form:false,
title:"添加",
plac:"请输入密码",
});
const fromData=reactive({
uid : 0,
account:'',
password:'',
name:'',
phone:'',
add_time:'',
last_time:'',
status:1,
});
const tableData=reactive({
lists:[]
});
UserLists().then((e)=>{
tableData.lists=e;
})
// 添加和修改弹框
const add=(e)=>{ //e传一个0的值(看第4行)
console.log(e);
data.is_form=true;//
if(e!=0){
data.title="修改";
data.plac = "密码如果不修改,可以不填写"
fromData.account=e.account;
fromData.name=e.name;
fromData.phone=e.phone;
fromData.password="";
fromData.uid = e.uid;
}else{
data.title="添加";
data.plac = "请输入密码";
fromData.account="";
fromData.name="";
fromData.phone="";
fromData.add_time="";
fromData.last_time="";
fromData.password="";
fromData.uid = 0;
}
}
//'确定'按钮点击时调用以下fun方法
const fun=()=>{
console.log(fromData);
UserSave(fromData).then((e)=>{
//console.log(e);
// alert(e.msg);
// console.log(fromData.add_time);
// console.log(fromData.last_time);
data.is_form=false;//弹窗关闭
UserLists().then((e)=>{
tableData.lists=e;
})
if(e.code==0){
ElMessage({
message: e.msg,
type: 'success',
})
}else{
ElMessage({
message: e.msg,
type: 'error',
})
}
})
// data.is_form=false;//弹窗关闭
// UserLists().then((e)=>{
// tableData.lists=e;
// })
}
const del=(e)=>{
console.log(e);
UserDel({uid:e.uid}).then((e)=>{
if(e.code==0){
ElMessage({
message:e.msg,
type:'success'
});
}else{
ElMessage({
message:e.msg,
type:'error'
});
}
UserLists().then((e)=>{
tableData.lists=e;
})
})
}
</script>
5.后台Api文件:Api2.php
import { request } from "../network/request.js";
// export function index(){
// return request({
// url:"Api2/index",
// method:"get",
// })
// }
//查询用户列表接口
export function UserLists(){
return request({
url:"Api2/UserLists",
method:"post",
})
}
// 添加用户数据接口
export function UserSave(data){
//header("Access-Control-Allow-Origin:*");
return request({
url:"Api2/UserSave",
method:"post",
data:data
})
}
// 删除用户数据接口
export function UserDel(data){
return request({
url:"Api2/UserDel",
method:"post",
data:data
})
}
//创建登录接口
export function Login(data){
return request({
url:"Api2/Login",
method:"post",
data:data
})
}
6.后台的Ticket文件:Ticket.php
<?php
namespace app\controller;
use app\BaseController;
use ouyangke\Ticket;
// *要使用Db类必须使用门面方式( think\facade\Db )调用
use think\facade\Db;
// *引用门面方式的请求类
use think\facade\Request;
class Api2 extends BaseController{
//public function index(){
//header("Access-Control-Allow-Origin:*");
//------------------------------------
// echo 333;
//------------------------------------
// json格式,叫json字符串,在php中,不方便创建,先创建数组,用数组转成json格式
//------------------------------------
// $json = '{
// ouyangke => "欧阳克"
// }';
// echo $json;
//------------------------------------
// 数组格式
// $arr = [
// [
// 'uid' => 1,
// 'name' => '欧阳克'
// ],
// [
// 'uid' => 2,
// 'name' => '朱天蓬'
// ]
// ];
// print_r($arr);
//------------------------------------
// 把数组转换成json格式
// $arr = [
// [
// 'uid' => 1,
// 'name' => '欧阳克'
// ],
// [
// 'uid' => 2,
// 'name' => '朱天蓬'
// ]
// ];
// $json = json_encode($arr);
// print_r($json);
// [{"uid":1,"name":"\u6b27\u9633\u514b"},{"uid":2,"name":"\u6731\u5929\u84ec"}]
//------------------------------------
// 使用var_dump判断json是一个字符串////------------------------------------//------------------------------------ // 打印json数据的类型,是字符串
// $arr = [
// [
// 'uid' => 1,
// 'name' => '欧阳克'
// ],
// [
// 'uid' => 2,
// 'name' => '朱天蓬'
// ]
// ];
// $json = json_encode($arr);
// var_dump($json);
// string(77) "[{"uid":1,"name":"\u6b27\u9633\u514b"},{"uid":2,"name":"\u6731\u5929\u84ec"}]"
//------------------------------------
// 把json转换为数组的形式
// $json = '[{"uid":1,"name":"\u6b27\u9633\u514b"},{"uid":2,"name":"\u6731\u5929\u84ec"}]';
// $array = json_decode($json,true);
// print_r($array);
//------------------------------------
// 用户表转换为数组形式
//$arr_user = Db::table('bew_user')->select()->toArray();
//打印
// print_r($arr_user);
// if(!empty( $arr_user)){
// foreach( $arr_user as &$user_v){
// if($user_v['status'] == 1){
// $user_v['status_s'] = '开启';
// }else{
// $user_v['status_s'] = '关闭';
// }
// //把时间的值由时间戳改为年月日
// $user_v['add_time'] = date('Y-m-d',$user_v['add_time']);
// $user_v['last_time'] = date('Y-m-d',$user_v['last_time']);
// }
// }
// 用户表由数组形式转换为json形式
//$json_user = json_encode( $arr_user);
//输出
// echo $json_user;
//也可以打印的方法
// print_r($json_user);
//------------------------------------
//}
//从前台的查询用户列表接口转到后台这用户列表方法
public function UserLists(){
//header("Access-Control-Allow-Origin:*");
// 用户表转换为数组形式
$arr_user = Db::table('bew_user')->select()->toArray();
if (!empty($arr_user)) {
foreach ($arr_user as & $user_v) {
if ($user_v['status'] == 1) {
$user_v['status'] = '开启';
} else {
$user_v['status'] = '关闭';
}
//把时间的值由时间戳改为年月日
$user_v['add_time'] = date('Y-m-d', $user_v['add_time']);
$user_v['last_time'] = date('Y-m-d', $user_v['last_time']);
}
}
// 用户表由数组形式转换为json形式
$json_user = json_encode($arr_user);
//输出
// echo $json_user;
//也可以打印的方法
print_r($json_user);
}
//从前台的添加用户接口转到后台这添加用户方法
public function UserSave(){
//$post相当于前台的弹窗表单
$post=input('post.');
//print_r($post);
if ($post['uid']==0) {
$data=[ //以下$data就是要传的数据
'account' => $post['account'],//如'account'是$post下的'account'传过来的
'password'=>md5($post['password']),
'name' => $post['name'],
'phone' => $post['phone'],
'status' => $post['status'],
// 'add_time'=>time(),//time()表示当前日期
//strtotime表示获取某个时间的时间戳
'add_time'=>strtotime($post['add_time']),
// 'last_time'=>strtotime($post['last_time'])//数据库中如果对该字段(add_time)设为不可null,则该项就要加上去(即不能注释)
'last_time'=>strtotime($post['last_time']),
];
// print_r ($data);
$insert = Db::table('bew_user')->insert($data);
if (empty($insert)) {
$arr=[
'code'=>1,//以上添加中的插入语句通常返回为code码,有0或1,0表示成功,1表示失败,这里用数组包括0及1
'msg'=>'失败'
];
} else {
$arr=[
'code'=>0,
'msg'=>'成功'
];
}
echo json_encode($arr);// 由数组形式转换为json形式
}else{
$data=[ //以下$data就是要传的数据
//'uid'=>$post['uid'],
'account' => $post['account'],//如'account'是$post下的'account'传过来的
//密码不修改
// 'password'=>md5($post['password']),
'name' => $post['name'],
'phone' => $post['phone'],
'status' => $post['status'],
'add_time'=>strtotime($post['add_time']),
'last_time'=>strtotime($post['last_time']),
];
if(!empty($post['password'])){
//当修改对象时不使其密码为空
$data['password']=md5($post['password']);
}
$user=Db::table('bew_user')->where('uid',$post['uid'])->update($data);
if (empty($user)) {
$arr=[
'code'=>1,//以上添加中的插入语句通常返回为code码,有0或1,0表示成功,1表示失败,这里用数组包括0及1
'msg'=>'修改失败'
];
} else {
$arr=[
'code'=>0,
'msg'=>'修改成功'
];
}
echo json_encode($arr);// 由数组形式转换为json形式
}
}
//从前台的删除用户接口转到后台这删除用户方法
public function UserDel(){
$uid=input('post.uid');
if(empty($uid)){
$arr=[
'code'=>1,
'msg'=>'请选择用户'
];
echo json_encode($arr);
exit;
}
$del=Db::table('bew_user')->where('uid',$uid)->delete();
if(empty($del)){
$arr=[
'code'=>1,
'msg'=>'删除失败'
];
echo json_encode($arr);
exit;
}else{
$arr=[
'code'=>0,
'msg'=>'删除成功'
];
echo json_encode($arr);
}
}
//从前台的登录接口转到后台这登录方法
public function Login(){
$post=input('post.');//接收这post
if(empty($post['account'])){ //如果接收的post中的账号为空
$arr=[
'code'=>1,
'msg'=>'账户不能为空'
];
echo json_encode($arr);
exit;
}
if(empty($post['password'])){ //如果接收的post中的密码为空
$arr=[
'code'=>1,
'msg'=>'密码不能为空'
];
echo json_encode($arr);
exit;
}
//根据所接收的账号,查询bew_user表,并返回给变量$user
$user=Db::table('bew_user')->where('account',$post['account'])->find();
//print_r($user);//把它打印出来
if(empty($user)){ //判断变量$user是否为空
$arr=[
'code'=>1,
'msg'=>'未找到账户'
];
echo json_encode($arr);
exit;
}
if($user['password']!=md5($post['password'])){
$arr=[
'code'=>1,
'msg'=>'密码错误'
];
echo json_encode($arr);
exit;
}
// unset($user['password']);//登录时数据中不显示密码(暂时打开)
//票据调用创建(create())方法,把$user下面的uid传过来,关键词是phpcn,最后返回一个票据$ticket
$ticket=Ticket::create($user['uid'],'phpcn');
// print_r($ticket); //暂时打开
//echo Ticket::get($ticket,'phpcn');//把32位加密传回去,然后再输出出来(暂时打开)
$user['ticket'] = $ticket;
unset($user['password'],$user['uid']);//登录时数据中不显示密码与用户的uid
$arr = [
'code' => 0,
'msg' => '登录成功',
'data' => $user
];
echo json_encode($arr);
}
}
题目1:登录功能与及使用token对uid的加密
<?php
namespace ouyangke;
/**
* ticket
* @author ouyangke
*/
// Tickey(票据)其作用是考虑数据安全,这里是不直接显示uid,并对uid进行加密
class Ticket {
/**
* 只允许静态调用
*/
// final private function __construct() {
// throw new ThinkException('只允许静态调用');
// }
/**
* 创建ticket(即门票的意思)
*
* @param integer $uid 用户id
* @param integer $bind 关键词
* @param number $time 有效期
* @param string $durable
* @return multitype:number string
*/
//静态的创建方法(由于是静态,故可用'::'进行访问及调用,其中7*24*60*60表示7天有效期,$durable为判断)
// public static function create($uid, $bind="ouyangke",$time=7*24*60*60, $durable = false) {
public static function create($uid, $bind="ouyangke",$time=1*1*1*60, $durable = false) {
$expire = time() + $time;
//调用_genKey方法(即加密成一个key,现返回给$key)
$key = self::_genKey($uid, $bind, $expire);
// 然后把上面的$bind, $uid, $key, $expire, $durable传到以下这个生成门票的方法中
// 以下通过调用生成门票方法,然后就生成一个票据($ticket)
$ticket = self::_buildTicket($bind, $uid, $key, $expire, $durable);
return $ticket;
}
/**
* 获取ticket信息
*
* @param integer $ticket
* @param boolean $checked
* @return Ambigous <mixed, NULL, multitype:number >
*/
// 静态获取票据方法(把票据传过来)
public static function get($ticket, $bind, $checked = false) {
$checked || self::checkFormat($ticket);
$uid = 0;
$key = 0;
$expire = 0;
$durable = false;
// 把以下这些数据进行解密
$info = self::_parseTicket($ticket, $bind, $uid, $key, $expire, $durable);
if (time() < $expire) {
$info = array();
$info['uid'] = $uid;
$info['expire'] = $expire;
return $uid; //最后返回uid
}
return false;
}
/**
* 生成ticket
* @param string $bind
* @param integer $uid
* @param integer $key
* @param integer $expire
* @param boolean $durable
* @return string
*/
//生成门票的方法
protected static function _buildTicket($bind, $uid, $key, $expire, $durable = false) {
$uBit = decbin($uid);
$uLen = strlen($uBit);
$eBit = sprintf('%032b', $expire);
$kBit = sprintf('%032b', $key);
$rBit = sprintf('%058s%05b%d', substr(base_convert(substr(md5(uniqid(mt_rand(), true)), mt_rand(0, 16), 16), 16, 2), 0, 58), $uLen % 32, $durable ? 1 : 0);
$cBit = sprintf('%032b', crc32($uBit . $eBit . $kBit . $rBit . $bind));
$uBit .= substr(sprintf('%032b', mt_rand()), $uLen - 32);
$bin = '';
for ($i = 0; $i < 32; $i++) {
$bin .= $rBit[$i * 2];
$bin .= $eBit[$i];
$bin .= $kBit[$i];
$bin .= $rBit[$i * 2 + 1];
$bin .= $uBit[$i];
$bin .= $cBit[$i];
}
// 然后再进行加密并返回
return strtr(base64_encode(implode('', array_map(function($item){ return chr(bindec($item)); }, str_split($bin, 8)))), '+/', '-_');
}
/**
* 解析Ticket
*
* @param string $ticket
* @param string $bind
* @param integer $uid
* @param integer $key
* @param integer $expire
* @param boolean $durable
* @return boolean
*/
protected static function _parseTicket($ticket, $bind, &$uid = NULL, &$key = NULL, &$expire = NULL, &$durable = NULL) {
$rBit = '';
$eBit = '';
$kBit = '';
$uBit = '';
$cBit = '';
$bin = implode('', array_map(function($item){ return sprintf('%08b', ord($item)); }, str_split(base64_decode(strtr($ticket, '-_', '+/')), 1)));
for ($i = 0; $i < 192; $i += 6) {
$rBit .= $bin[$i];
$eBit .= $bin[$i + 1];
$kBit .= $bin[$i + 2];
$rBit .= $bin[$i + 3];
$uBit .= $bin[$i + 4];
$cBit .= $bin[$i + 5];
}
$uLen = bindec(substr($rBit, 58, 5));
if ($uLen < 32) {
$uBit = substr($uBit, 0, $uLen);
}
if (sprintf('%u', crc32($uBit . $eBit . $kBit . $rBit . $bind)) == bindec($cBit)) {
$uid = bindec($uBit);
if ($uid > 0) {
$expire = bindec($eBit);
$key = bindec($kBit);
$durable = ($rBit[63] === '1');
return true;
}
}
// throw new ThinkException('Ticket error');
}
/**
* 生成key
*
* @param integer $uid
* @param string $bind
* @param integer $expire
* @return integer
*/
//_genKey方法其作用是加密成一个key(键)
// 先用'%d|%s|%d',替换掉$uid, $bind, $expire,然后用md5进行加密,最后使用32位数据进行处理并返回
protected static function _genKey($uid, $bind, $expire) {
return crc32(md5(sprintf('%d|%s|%d', $uid, $bind, $expire), true));
}
/**
* 发送ticket http头
*
* @param string $ticket
* @param integer $uid
* @param integer $ttl
*/
// protected static function _headerTicket($ticket, $uid, $ttl) {
// header(sprintf('Set-Ticket: %s; uid=%d; expires=%s; Max-Age=%d', $ticket, $uid, Request::httpDate(time() + $ttl), $ttl));
// }
/**
* 判断格式是否正确
*
* @param string $ticket
* @return boolean
*/
protected static function _isTicket($ticket) {
return strlen($ticket) == 32 && strspn($ticket, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_') == 32;
}
/**
* 检查格式是否正确
*
* @param string $ticket
*/
public static function checkFormat($ticket) {
if (!self::_isTicket($ticket)) {
return false;
}
}
}
1
2
3
4
1
2
3
4
1
2
3