仿QQ异地登录强制下线功能的实现

一,仿QQ异地登录强制下线功能

效果:同一个账号,最后一个登录的用户会把前一个登录后的用户顶掉,被迫下线!

1,剖析登录原理

登录的后台逻辑

  1. 准备一张账户信息表(账号,密码,是否禁用等字段)
  2. 查询账号密码,如果正确就记录session,并重定向到首页。

而实现上述效果只需要稍加修改即可。

  1. 第一处修改,在账户信息表后新增一个字段,存储登录用户的sessionId的值。
  2. 第二处修改,在登录时,查询账号密码正确并重定向到首页之前添加一个步骤,就是更新当前账号对应的sessionId字段。(不同浏览器的sessionId都是唯一的)
  3. 退出时,在清除session之前,把当前账号对应的sessionId字段值置Null
  4. 写一个轮询方法,根据当前浏览器的sessionId到账户信息表查询是否可以查到,如果查得到,说明账号状态正常,如果没有查到,只能是该账号被另一个浏览器进行访问登录了(会覆盖掉原来的sessionId,改为登录人本机浏览器的sessionId

2,数据库表设计

在这里插入图片描述

3,代码实现

登录请求方法

	 /**
     * todo 登录请求
     */
    @PostMapping("/loginSubmit")
    public String loginSubmit(String username, String password, HttpSession session, Model model){
    
    
        List<Map> accountByPassword = busineService.getAccountByPassword(username, password);
        if(accountByPassword.size()>0){
    
    
            //用户存在
            session.setAttribute("account",username);
            model.addAttribute("account",username);
			//##############   重点  #########################
            String sessionid = session.getId();
            int i = busineMapper.updateSessionIdByUsername(sessionid, username);
			//##############   重点  #########################
            return "redirect:/business/index";
        }else{
    
    
            //用户不存在
            model.addAttribute("msg","用户名或密码错误");
            return "login";
        }
    }

退出登录方法

 	/**
     * todo 退出系统
     */
    @RequestMapping("/logout")
    public String logout(HttpSession session){
    
    
        String username=(String) session.getAttribute("account");
        	//##############   重点  #########################
        int i = busineMapper.updateSessionIdByUsername(null, username);
			//##############   重点  #########################
        session.removeAttribute("account");
        return "forward:/business/login";
    }
    /**
     * todo 退出系统[通知下线时点击确定跳转此方法,不可以清空sessionId的值,因为后来登录的人要正常使用系统]
     */
    @RequestMapping("/logout1")
    public String logout1(HttpSession session){
    
    
        session.removeAttribute("account");
        return "forward:/business/login";
    }

登录sql实现

	 // todo 登录
    @Select(value = "select * from activiti_account where username=#{username} and password=#{password} and yxbz=1")
    List<Map> getAccountByPassword(@Param("username") String username, @Param("password") String password);

    //登录时更新账号表的sessionid字段
    @Update(value = "update activiti_account set sessionid=#{sessionid} where username=#{username}")
    int updateSessionIdByUsername(@Param("sessionid") String sessionid,@Param("username") String username);

    //轮询检查sessionid是否存在或是否被替换
    @Select(value = "select * from activiti_account where sessionid=#{sessionid}")
    Map checkSessionId(String sessionid);

js写一个定时器,定时刷新账号登录状态

$(function () {
    
    
			var is=true;
			setInterval(function () {
    
    
				if(is){
    
    
                    $.ajax({
    
    
                        url:"/business/checkSessionId",
                        type:"post",
                        dataType:"json",
                        success:function (data) {
    
    
                            if(data==0){
    
    
                                is=false;
                                layer.confirm('您的账号已在异地登录,请重新登录', {
    
    
                                    btn: ['确定'] //按钮
                                }, function(){
    
    
                                    //退出并跳转登录页面
                                    window.location.href = "/business/logout1";
                                }, function(){
    
    
                                    //退出并跳转登录页面
                                    window.location.href = "/business/logout1";
                                });
                            }
                        }
                    });
				}
            },5000);

轮询方法

 	//轮询检查sesiosnid是否被替换,如果被替换,说明该账号已在异地登录
    @RequestMapping("/checkSessionId")
    @ResponseBody
    public int checkSessionId(HttpSession session){
    
    
        Map map = busineMapper.checkSessionId(session.getId());
        if(map!=null){
    
    
            //没有被替换,账号状态正常
            return 1;
        }else{
    
    
            //账号已在异地登录,掉线通知
            return 0;
        }
    }

效果截图
在这里插入图片描述

二,仿淘宝吱口令的功能实现

效果:在A平台上复制一段某商品分享的吱口令,然后打开B平台,然后B平台会自动识别用户复制的吱口令,并打开该口令对应商品的详情页。

步骤分析:

  1. 在A平台封装某商品的详情信息包括页面链接,然后加密处理,生成一段字符串,供用户复制。
  2. 打开B平台,B平台监听剪切板中的吱口令信息,【监听方式使用JS定时器请求后台方法,该方法到剪切板中查看吱口令内容】,如果有吱口令信息,则解密吱口令并打开该口令对应商品的详情页。

这里没有具体实现,只给出实现思路,不过实现起来挺简单的!
剪切板如何操作?

Java 操作剪贴板的类在 java.awt.* 包(及其子包)下,获取系统剪贴板代码

下面代码实现用 Java 代码获取系统剪贴板对象,实现 复制粘贴 文本的例子。

package com.xiets.clipboard;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
public class Main {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        // 把文本设置到剪贴板(复制)
        setClipboardString("Hello System Clipboard!");
        // 从剪贴板中获取文本(粘贴)
        String text = getClipboardString();
        System.out.println("text: " + text);
    }
    /**
     * 把文本设置到剪贴板(复制)
     */
    public static void setClipboardString(String text) {
    
    
        // 获取系统剪贴板
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        // 封装文本内容
        Transferable trans = new StringSelection(text);
        // 把文本内容设置到系统剪贴板
        clipboard.setContents(trans, null);
    }
    /**
     * 从剪贴板中获取文本(粘贴)
     */
    public static String getClipboardString() {
    
    
        // 获取系统剪贴板
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        // 获取剪贴板中的内容
        Transferable trans = clipboard.getContents(null);
        if (trans != null) {
    
    
            // 判断剪贴板中的内容是否支持文本
            if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) {
    
    
                try {
    
    
                    // 获取剪贴板中的文本内容
                    String text = (String) trans.getTransferData(DataFlavor.stringFlavor);
                    return text;
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

控制台输出
在这里插入图片描述
参考文章:https://blog.csdn.net/xietansheng/article/details/70478266

猜你喜欢

转载自blog.csdn.net/weixin_44001965/article/details/106786506