php限制会员多处登入,织梦网站管理系统禁止会员同一帐号多地登录

修改织梦网站管理系统PHP程序,实现禁止同一会员帐号多地登录,一般来讲就要从判断IP入手。我修改的这个原理是:会员登录后增加创建名为Only的cookie,当会员刷新页或浏览新页面时判断从数据表@_member中当前会员的loginip值md5后与Only比较异同,把此条件加在验证用户是否已经登录函数IsLogin()中,成立返回真,不成立返回假,即可实现。修改如下:

修改文件/include/memberlogin.class.php

构造函数,大约171行左右的$this->OnlyCookie = GetCookie("Only");代码下一行增加代码如下:

$this->OnlyCookie = GetCookie("Only");

验证用户是否已经登录函数IsLogin(),大约第290行 改为

function IsLogin()

{

$loginipCookie = substr(md5($this->fields['loginip']),0,16);

if($this->M_ID > 0 && $this->OnlyCookie == $loginipCookie) return TRUE;

else return FALSE;

}

重置用户信息函数ResetUser()内的最后,大约第389行左右代码DropCookie('DedeLoginTime');的下一行增加代码如下(除管理员外的cookie方法):

DropCookie('Only');

意为重置会员cookie信息

大约第530行左右保存用户cookie的函数PutLoginInfo()内的if($this->M_KeepTime > 0)内增加代码如下(除管理员外的cookie方法);

PutCookie('Only',substr(md5(GetIp()),0,16),$this->M_KeepTime);

意为增加cookie条目

下面的else内最后加上

PutCookie('Only',$this->M_LoginTime);

修改文件/include/userlogin.class.php(这个没有测试管理员的帐号效果,后来我没我修改这条,也就是说这条改不改不影响除ID为1的其他会员的禁止多地登录效果)

keepUser()函数内大约第315行增加代码如下:

PutCookie('Only', substr(md5('MrDede'.GetIp()),0,16), 3600 * 24, '/');

保持用户的会话状态,这里给管理员(id=1)增加了名为Only的cookie信息

————————–

2020-02-26 修改

2021-02-01 再次修改

因为之前修改时的说明有些笼统,说明中隐含的借用了上面的说明,逻辑也有一些问题,造成使用此方法修改的朋友不能实现单点登录的功能,在这里表示抱歉。

这次(2021-02-01)有朋友来问我为什么不能实现单点登录功能,看自己的说明,实测了一下,确实。所以,又进行了回忆、修改、添加详细说明。现在实测可用。

当时可能是因为任务匆忙,没有写详细说明,只是在心里想着,没有写出来,造成现在自己看自己的说明都看不懂。这也是对自己的一个教训,以后一定要写详细的说明。

以下修改方法与以上方法无关

————————–

有朋友看到文章来找我帮着修改单点登录的功能,上面是过去的思路,随着时间的变化,对此功能的实现也有了新的想法。

因为现在的路由多是动态路由,所以靠IP来识别,就显示不严谨、不可靠了。另外,还要看用户的需求,一种需求是在同一IP下可以多设备登录,另一种需求就是即使在同一IP下也要实现单个设备登录。

新的思路(单设备登录):

1、数据表dede_member新建两个字段,token,token_time

ALTER TABLE `dede_member`

ADD COLUMN `token` varchar(120) NOT NULL DEFAULT '' COMMENT 'Token字符串' AFTER `checkmail`,

ADD COLUMN `token_time` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Token创建时间' AFTER `token`;

0

1

2

ALTERTABLE`dede_member`

ADDCOLUMN`token`varchar(120)NOTNULLDEFAULT''COMMENT'Token字符串'AFTER`checkmail`,

ADDCOLUMN`token_time`int(10)UNSIGNEDNOTNULLDEFAULT0COMMENT'Token创建时间'AFTER`token`;

上面SQL语句中的checkmail是表dede_member的最后一个字段,当然新字段也可以接在别的字段后,这个随自己的意。

2、在/include/memberlogin.class.php的MemberLogin类内部的最后增加方法:

/**

* 单点登录更新token - 织梦先生 添加

* 注:在确保按原有逻辑登录成功后,使用此方法

* @param boolean $set 是否直接设置 token,登录时$set=true,非登录时不会设置此参数

* @return boolean

*/

function resetToken($set = false) {

global $dsql;

$token_time = time();

if(!empty($set)) { // 登录时,直接设置 Cookie

// 强制生成一个随机数做为新 token,并和当前时间组合加密

$token = rand(1000, 9999);

$newEncode = sha1($token . $token_time);

// 更新会员表 token_time 字段

$dsql->ExecuteNoneQuery("UPDATE `#@__member` SET token='$token',token_time='$token_time' WHERE mid='{$this->M_ID}' ");

// 更新前端 Cookie 中的 DedeToken 值

if($this->M_KeepTime > 0) {

PutCookie('DedeToken',$newEncode,$this->M_KeepTime);

} else {

PutCookie('DedeToken',$newEncode);

}

$this->isToken = true;

$this->fields['token_time'] = $token_time;

return $this->isToken;

}

if(!empty($this->fields) && empty($this->fields['token'])) {

// 这个逻辑只会进入一次,也就当会员表字段token为空的时间,

// 理论上讲,只要会员登录一次,这个字段就永远不会为空,除非人为的修改

// 所以才说,这个逻辑只会进入一次。

$token = 'Genesis'; // 创世

$encode = sha1($token . $token_time); // 与时间组合加密

// 更新会员的 token 字段和与token组合的时间字段 token_time

$dsql->ExecuteNoneQuery("UPDATE `#@__member` SET token='$token',token_time='$token_time' WHERE mid='{$this->M_ID}' ");

// 织梦系统是否设置了Cookie过期时间,如果设置了的话,在生成cookie的时候要带上

if($this->M_KeepTime > 0) {

PutCookie('DedeToken',$encode,$this->M_KeepTime);

} else {

PutCookie('DedeToken',$encode);

}

$this->fields['token_time'] = $token_time;

$this->isToken = false;

} else { // 每次访问都会进入这个逻辑

// 获取前端保存的 token

$tokenCookie = GetCookie('DedeToken');

if(empty($tokenCookie)) {

$this->isToken = false;

} else {

// 组合 token 和 token_time,并加密

$encode = sha1($this->fields['token'] . $this->fields['token_time']);

// 与前端传递的加密token对比

$this->isToken = $tokenCookie === $encode;

if($this->isToken) { // 验证通过

// 保存 cookie 的 DedeToken 值到token,并更新token_time字段为当前时间戳

$dsql->ExecuteNoneQuery("UPDATE `#@__member` SET token='$tokenCookie',token_time='$token_time' WHERE mid='{$this->M_ID}' ");

// 生成新的准备下发到cookie DedeToken的加密值

$newEncode = sha1($tokenCookie . $token_time);

if($this->M_KeepTime > 0) {

PutCookie('DedeToken',$newEncode,$this->M_KeepTime);

} else {

PutCookie('DedeToken',$newEncode);

}

}

}

}

return $this->isToken;

}

0

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

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

/**

* 单点登录更新token - 织梦先生 添加

* 注:在确保按原有逻辑登录成功后,使用此方法

* @param boolean $set 是否直接设置 token,登录时$set=true,非登录时不会设置此参数

* @return boolean

*/

functionresetToken($set=false){

global$dsql;

$token_time=time();

if(!empty($set)){// 登录时,直接设置 Cookie

// 强制生成一个随机数做为新 token,并和当前时间组合加密

$token=rand(1000,9999);

$newEncode=sha1($token.$token_time);

// 更新会员表 token_time 字段

$dsql->ExecuteNoneQuery("UPDATE `#@__member` SET token='$token',token_time='$token_time' WHERE mid='{$this->M_ID}' ");

// 更新前端 Cookie 中的 DedeToken 值

if($this->M_KeepTime>0){

PutCookie('DedeToken',$newEncode,$this->M_KeepTime);

}else{

PutCookie('DedeToken',$newEncode);

}

$this->isToken=true;

$this->fields['token_time']=$token_time;

return$this->isToken;

}

if(!empty($this->fields)&&empty($this->fields['token'])){

// 这个逻辑只会进入一次,也就当会员表字段token为空的时间,

// 理论上讲,只要会员登录一次,这个字段就永远不会为空,除非人为的修改

// 所以才说,这个逻辑只会进入一次。

$token='Genesis';// 创世

$encode=sha1($token.$token_time);// 与时间组合加密

// 更新会员的 token 字段和与token组合的时间字段 token_time

$dsql->ExecuteNoneQuery("UPDATE `#@__member` SET token='$token',token_time='$token_time' WHERE mid='{$this->M_ID}' ");

// 织梦系统是否设置了Cookie过期时间,如果设置了的话,在生成cookie的时候要带上

if($this->M_KeepTime>0){

PutCookie('DedeToken',$encode,$this->M_KeepTime);

}else{

PutCookie('DedeToken',$encode);

}

$this->fields['token_time']=$token_time;

$this->isToken=false;

}else{// 每次访问都会进入这个逻辑

// 获取前端保存的 token

$tokenCookie=GetCookie('DedeToken');

if(empty($tokenCookie)){

$this->isToken=false;

}else{

// 组合 token 和 token_time,并加密

$encode=sha1($this->fields['token'].$this->fields['token_time']);

// 与前端传递的加密token对比

$this->isToken=$tokenCookie===$encode;

if($this->isToken){// 验证通过

// 保存 cookie 的 DedeToken 值到token,并更新token_time字段为当前时间戳

$dsql->ExecuteNoneQuery("UPDATE `#@__member` SET token='$tokenCookie',token_time='$token_time' WHERE mid='{$this->M_ID}' ");

// 生成新的准备下发到cookie DedeToken的加密值

$newEncode=sha1($tokenCookie.$token_time);

if($this->M_KeepTime>0){

PutCookie('DedeToken',$newEncode,$this->M_KeepTime);

}else{

PutCookie('DedeToken',$newEncode);

}

}

}

}

return$this->isToken;

}

3、类中增加成员变量 var $isToken = false;

4、在构造方法内引用resetToken()方法,一定要放在构造方法内的最后引用。

为了尽可能是符合织梦系统的逻辑,所以选择把此验证方法加在如下位置。在开发过程中,无论怎么修改,都要尽可能的保持原信息的逻辑关系。

//php5构造函数

function __construct($kptime = -1, $cache=FALSE)

{

global $dsql;

......

if(empty($this->M_ID))

{

$this->ResetUser();

}else{

$this->M_ID = intval($this->M_ID);

......

if(is_array($this->fields)){

......

if( !$formcache )

{

SetCache($this->memberCache, $this->M_ID, $this->fields, 1800);

}

$this->resetToken(); // 加在这里

}else{

$this->ResetUser();

}

}

}

0

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

//php5构造函数

function__construct($kptime=-1,$cache=FALSE)

{

global$dsql;

......

if(empty($this->M_ID))

{

$this->ResetUser();

}else{

$this->M_ID=intval($this->M_ID);

......

if(is_array($this->fields)){

......

if(!$formcache)

{

SetCache($this->memberCache,$this->M_ID,$this->fields,1800);

}

$this->resetToken();// 加在这里

}else{

$this->ResetUser();

}

}

}

5、在PutLoginInfo()方法中引用resetToken()方法,如:

function PutLoginInfo($uid, $logintime=0)

{

global $cfg_login_adds, $dsql;

$this->resetToken();

......

0

1

2

3

4

functionPutLoginInfo($uid,$logintime=0)

{

global$cfg_login_adds,$dsql;

$this->resetToken();

......

6、修改ResetUser()方法

/**

* 重置用户信息

*

* @return void

*/

function ResetUser()

{

$this->fields = '';

$this->M_ID = 0;

$this->M_LoginID = '';

$this->M_Rank = 0;

$this->M_Face = "";

$this->M_Money = 0;

$this->M_UserName = "";

$this->M_LoginTime = 0;

$this->M_MbType = '';

$this->M_Scores = 0;

$this->M_Spacesta = -2;

$this->M_UpTime = 0;

$this->M_ExpTime = 0;

$this->M_JoinTime = 0;

$this->M_HasDay = 0;

DropCookie('DedeUserID');

DropCookie('DedeLoginTime');

DropCookie('DedeToken'); // 添加这个

}

0

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

/**

*  重置用户信息

*

* @return    void

*/

functionResetUser()

{

$this->fields='';

$this->M_ID=0;

$this->M_LoginID='';

$this->M_Rank=0;

$this->M_Face="";

$this->M_Money=0;

$this->M_UserName="";

$this->M_LoginTime=0;

$this->M_MbType='';

$this->M_Scores=0;

$this->M_Spacesta=-2;

$this->M_UpTime=0;

$this->M_ExpTime=0;

$this->M_JoinTime=0;

$this->M_HasDay=0;

DropCookie('DedeUserID');

DropCookie('DedeLoginTime');

DropCookie('DedeToken');// 添加这个

}

7、/member/index_do.php中,在$cfg_ml->DelCache($cfg_ml->M_ID);后添加引用$cfg_ml->resetToken(true);如:

else

{

// 清除会员缓存

$cfg_ml->DelCache($cfg_ml->M_ID);

$cfg_ml->resetToken(true);

......

0

1

2

3

4

5

else

{

// 清除会员缓存

$cfg_ml->DelCache($cfg_ml->M_ID);

$cfg_ml->resetToken(true);

......

8、修改登录验证的方法IsLogin(),修改如下:

/**

* 验证用户是否已经登录

*

* @return bool

*/

function IsLogin()

{

if($this->M_ID > 0 && $this->isToken) return TRUE;

else return FALSE;

}

0

1

2

3

4

5

6

7

8

9

/**

*  验证用户是否已经登录

*

* @return    bool

*/

functionIsLogin()

{

if($this->M_ID>0&&$this->isToken)returnTRUE;

elsereturnFALSE;

}

9、前端控制:后端创建接口,验证验证登录状态;前端轮询访问接口,编写自己的逻辑。后端控制:根据实际业务编写逻辑。

简单一点说这个逻辑,就是用户每次访问,都验证签名,验证通过后创建一个新签名,把签名生成条件保存入库,同时把签名写入Cookie,如此循环。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的网吧会员管理系统登录页面的 PHP 代码示例: ```php <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>网吧会员管理系统登录页面</title> </head> <body> <h1>网吧会员管理系统登录页面</h1> <form action="login.php" method="post"> <label for="username">用户名:</label> <input type="text" id="username" name="username"><br><br> <label for="password">密码:</label> <input type="password" id="password" name="password"><br><br> <input type="submit" value="登录"> </form> </body> </html> ``` 在这个示例中,我们使用 HTML 构建了一个简单的登录页面,包含用户名和密码输入框以及登录按钮。当用户点击登录按钮时,表单将提交到名为 `login.php` 的 PHP 脚本进行处理。 接下来,我们需要编写 `login.php` 脚本来验证用户输入的用户名和密码是否正确,并根据验证结果进行相应的处理。以下是一个简单的 `login.php` 脚本示例: ```php <?php // 获取用户输入的用户名和密码 $username = $_POST['username']; $password = $_POST['password']; // 假设数据库中保存了一个名为 "members" 的会员表,包含了用户名和密码字段 // 这里使用简单的数组模拟会员数据,实际应用中需要连接真实的数据库 $members = array( array('username' => 'user1', 'password' => 'pass1'), array('username' => 'user2', 'password' => 'pass2'), array('username' => 'user3', 'password' => 'pass3') ); // 遍历会员数据,查找与用户输入的用户名和密码匹配的记录 foreach ($members as $member) { if ($member['username'] == $username && $member['password'] == $password) { // 如果找到匹配的记录,则说明用户名和密码输入正确 // 在这里可以一些登录成功后的处理,比如跳转到会员管理页面 header('Location: members.php'); exit; } } // 如果遍历完整个会员数据都没有找到匹配的记录,则说明用户名或密码输入错误 // 在这里可以给出相应的提示信息,并让用户重新输入 echo '用户名或密码输入错误,请重新输入!'; ?> ``` 在这个示例中,我们首先获取用户输入的用户名和密码,然后遍历会员数据查找与之匹配的记录。如果找到匹配的记录,则说明用户名和密码输入正确,可以进行相应的处理(这里使用 `header()` 函数跳转到会员管理页面)。如果遍历完整个会员数据都没有找到匹配的记录,则说明用户名或密码输入错误,给出相应的提示信息并让用户重新输入。 需要注意的是,这只是一个简单的示例,实际应用中还需要进行更严格的输入验证和安全性处理,比如防止 SQL 注入、密码加密等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值