zoukankan      html  css  js  c++  java
  • springboot整合websocket实现登录挤退现象

    在项目期间遇到了同一个账号不能在不同的地方同时登录的情况,解决方法用到了websocket。

    关于websocket的原理网上有很多,我这里就不写了,推荐博客:

    https://www.cnblogs.com/myzhibie/p/4470065.html

    websocket理清原理:https://zhuanlan.zhihu.com/p/95622141

    这里我主要记录一下websocket来实现的登录挤退的功能

    一:实现的思想

    1.我的思路是这样的,在登录的时候要去后台验证账号密码的正确性,如果这个都不正确那就别说了。

    2.当账号和密码正确时,在session里面存储一下该用户信息,后台返回给前端一个标准,表示账号和密码正确,然后前端通过js来建立websocket的连接

    后台会接收这个连接,然后在这个连接中取出该连接服务器的session,通过session里面存储的用户id来判断静态变量websocket

    list里面是否含有该用户id的websocket(毕竟用户id为唯一标识)。

    3.如果含有,则说明该用户已经在登录的状态。所以通过后台的websocket对象来发送消息,告知前端js的websocket说用户已经登录了。

    4.如果不含有,则说明该账号目前不处于登录状态,就存放到静态变量List<Websocket>里面。并发送消息到前台说明登录成功。

    以上为最基本的思想。但是问题来了。如何实现?同时,如何在websocket获得该次连接服务器的HttpSession对象?

    慢慢来解决。这里默认该用户账号密码正确,从js发送websocket的连接开始。

    maven依赖:

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

    二:实现

      1.js发送websocket的连接

      

    function onenSocket(){
       /*newsinfo2为项目的根目录,websocket为请求地址,后台通过注解接收*/
    var socket = new WebSocket("ws://localhost:8080/newsinfo2/webSocket/"); if(typeof(socket) == undefined){ alert("您的浏览器不支持webSocket,请换一个浏览器..."); return ; }
        /*websocket接收消息的函数*/ socket.onmessage
    = function(msg){ if(msg == "已登录"){ alert("您的账号已在另一个地方尝试登录,如果不是您知晓的情况,请及时修改密码..."); }else if(msg == "登录成功"){ location.href="../index/index.html"; }else if(msg == "修改密码"){ alert("您账号的密码已经被修改!如果不是你自己知晓的情况,请及时修改密码..."); location.href="../login/login.html"; } } //socket打开时的方法 socket.onopen = function(){ } //socket关闭时的方法 socket.onclose = function(){ } //socket出错时的方法 socket.onerror = function(){ } /*//在页面加载时自动断开链接,这样就不会异常断开链接,后台不会报错误 $(document).ready(function(){ socket.close(); });*/ }

    该js发送请求后,后台接收如下:

      2.后台websocket的接收

    @ServerEndpoint(value = "/webSocket/")
    public class WebSocketServer {

    WebSocketServer为自创的类。通过这个注解,这个类会有一些自带的方法:

      onopen():连接时需要调用的方法

      onError():出现错误时执行的方法

      onClose():关闭连接时调用的方法

    该类中必须要自定义一个静态变量:

      

    //用于存储webSocketServer

    public static CopyOnWriteArraySet<WebSocketServer> webSocketServerSet = new CopyOnWriteArraySet<WebSocketServer>();

     这个框架就算是建立了,接下来是一些缝缝补补的工作。

    要完整的将我的webSocketServer呈现,那还需要获得httpsession对象。获得对象的方法和思想请看下面的博客:

    https://www.cnblogs.com/zhuxiaojie/p/6238826.html

    我的WebSocket整体呈现:

    package news.webSocket;
    
    import java.io.IOException;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    import javax.servlet.http.HttpSession;
    import javax.websocket.EndpointConfig;
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import news.bean.UserInfo;
    import news.config.Configuretor;
    import news.utils.LogerUtils;
    import news.utils.StaticValue;
    
    /**
     * webSocketServer类,用于处理登录挤退现象,
     * 思路:登录时,要判断该账号是否已创建一个webSocket对象存储起来了,根据这个判断的结果来进行下一步动作
     * @author 徐金仁
     */
    @ServerEndpoint(value = "/webSocket/" , configurator = Configuretor.class)
    public class WebSocketServer {
        //用于存储webSocketServer
        public static CopyOnWriteArraySet<WebSocketServer> webSocketServerSet = new CopyOnWriteArraySet<WebSocketServer>();
        private Session session;  //与某个客户端连接的会话,该session是属于WebSocket的session,不属于HttpSession
        private String sid; //用户的编号
        private Logger log = LogerUtils.getLogger(this.getClass());
        @Autowired
        private HttpSession httpSession_;
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((session == null) ? 0 : session.hashCode());
            result = prime * result + ((httpSession_ == null) ? 0 : httpSession_.hashCode());
            result = prime * result + ((sid == null) ? 0 : sid.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            WebSocketServer other = (WebSocketServer) obj;
            if (session == null) {
                if (other.session != null)
                    return false;
            } else if (!session.equals(other.session))
                return false;
            if (httpSession_ == null) {
                if (other.httpSession_ != null)
                    return false;
            } else if (!httpSession_.equals(other.httpSession_))
                return false;
            if (sid == null) {
                if (other.sid != null)
                    return false;
            } else if (!sid.equals(other.sid))
                return false;
            return true;
        }
    
        public static CopyOnWriteArraySet<WebSocketServer> getWebSocketServerSet() {
            return webSocketServerSet;
        }
    
        public static void setWebSocketServerSet(CopyOnWriteArraySet<WebSocketServer> webSocketServerSet) {
            WebSocketServer.webSocketServerSet = webSocketServerSet;
        }
    
        public String getSid() {
            return sid;
        }
    
        public void setSid(String sid) {
            this.sid = sid;
        }
    
        public HttpSession gethttpSession_() {
            return httpSession_;
        }
    
        public void sethttpSession_(HttpSession httpSession_) {
            this.httpSession_ = httpSession_;
        }
    
        public void setSession(Session session) {
            this.session = session;
        }
        
        /**
         * 获取HttpSession
         * @return
         */
        public HttpSession getHttpSession(){
            return this.httpSession_;
        }
        
        /**
         * 获取session
         * @return
         */
        public Session getSession(){
            return this.session;
        }
        
        /**
         * 连接的时候需要调用的方法
         * @throws IOException 
         */
        @OnOpen
        public void onOpen(Session session,  EndpointConfig config) throws IOException{
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            this.session = session;
            this.httpSession_ = httpSession;
            System.out.println("链接中..." + httpSession_.getId());
            //StaticValue为自定义的存放key值的类,里面都是一些常量
            Object obj =  (this.httpSession_.getAttribute(StaticValue.CURRENT_USER));
            if(obj != null){ //说明还链接中
                this.sid = String.valueOf(((UserInfo)obj).getUid());
                log.info(this.sid + "正在链接中...");
                if(!webSocketServerSet.contains(this)){
                    webSocketServerSet.add(this); //将连接到的添加进入set里面
                }
            }else{ //说明不链接了
                //等会在写
            }
            /*this.sendMessage("连接成功!");*/
        }
        /**
         * 发送消息的方法
         * @param string
         * @throws IOException 
         */
        public void sendMessage(String msg) throws IOException  {
            this.session.getBasicRemote().sendText(msg);
        }
        
        /**
         * 出现错误的方法
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session , Throwable error){
            log.error( this.sid + "websocket出错断开链接");
        }
        /**
         * 当连接断开时,调用的方法
         */
        @OnClose
        public void onClose(){
            webSocketServerSet.remove(this);
        }
        
        /**
         * 根据sid查询webSocket
         * @param sid
         * @return
         */
        public static WebSocketServer getWebSocket(String sid){
            for(WebSocketServer w : webSocketServerSet){
                if(sid.equals(w.sid)){
                    return w;
                }
            }
            return null;
        }
    }

    controller层:

    @RequestMapping("login")
        public UserInfo login(String uname, String upwd, HttpSession session) throws IOException{
            int result = 0;
            UserInfo userInfo = new UserInfo();
            userInfo.setUname(uname);
            userInfo.setUpwd(upwd);
            UserInfo us = null;
            us = loginService.login(userInfo);
            if(us == null){
                us = new UserInfo();
                us.setUid(-1); //表示账号或密码不对
            }else{//如果查寻到账号和密码都没有错误,则要判断是否已经被登录了,
                WebSocketServer wws = WebSocketServer.getWebSocket(String.valueOf(us.getUid()));
                if(wws != null){ //如果有
                    wws.sendMessage("已登录");
                    us.setUid(-2); //表示已登录
                }else{//表示暂时没有人登录,您是第一个,要将信息存储一下
                    session.setAttribute("userInfo", us);
                    session.setAttribute(StaticValue.CURRENT_USER, us);
                    System.out.println("session的id:" + session.getId());
                }
            }
            System.out.println(us);
            return us;

    这里还有几个坑,一个是如果就是登陆成功后,页面一刷新,websocket就会出异常断开,这里没有什么好的办法,只有每次刷新或者跳转页面的之后,都要重新链接。

    还有一个是localhost访问的情况和127.0.0.1访问的情况下是不同的。如果你在js中链接使用127.0.0.1,而项目运行后在浏览器地址上显示的是localhost的话,那么获得的HttpSession并不是同一个对象。这样的话会导致程序员的判断出现错误。解决的办法是同一使用127.0.0.1或者是localhost。至于为什么会出现这种不同,请查看下面:

    localhost 127.0.0.1和本机ip三者的区别

    localhost 
    不联网 
    不使用网卡,不受防火墙和网卡限制 
    本机访问 

    127.0.0.1 
    不联网 
    网卡传输,受防火墙和网卡限制 
    本机访问 

    本机IP 
    联网 
    网卡传输 ,受防火墙和网卡限制 
    本机或外部访问

    以上三者区别知识的来源:

    https://blog.csdn.net/qq_35101027/article/details/80745664

  • 相关阅读:
    05.设计模式_建造者模式
    04.设计模式_抽象工厂模式
    03.设计模式_工厂方法模式
    02.设计模式_单例模式
    01.设计模式_简单工厂模式
    cocos-js一些问题
    blender
    游戏编程模式
    Unity自动打包工具
    unity调用ios原生代码objective-c和回调
  • 原文地址:https://www.cnblogs.com/1998xujinren/p/12340671.html
Copyright © 2011-2022 走看看