登录解决方案-单体应用-Session登录(拦截器实现)-跨机房单点签退

 

一、需求实现

 跨机房单点签退,具体业务场景,用户A在他的手机登他的账号后,你再用你的手机登录他的账号把他的账号给挤下来,什么叫做跨机房,跨机房也可以叫做跨服务器,因为Session是存在服务端的

 如果生产上在不同服务器部署了同一个服务,服务A和服务B IP地址 端口不同 但是是同一个项目,A用户将他的账号登录到了A服务器,B用户将A用户的账号登录到了B服务器,怎么将A用户的账号从A服务器 挤下来,就是清掉A服务器里A用户的Session。

二、解决方案

1、用户登录存session之前,判断session这个Key存不存在(可以用Sessions.containsKey(sessionkey)来判断),存在就移除session 在存,这样 就把上一个人的session清掉了,但是这种方案不适应分布式的情况,分布式下上一个人是登录在A机的,我在B机是清不掉A机的Session的

2、跨机房单点签退

  一个session就是一个会话,而会话是用JSESSIONID 取到的

在接口的请求头里面传一个用户已经登录的 JSESSIONID 服务端就能取到session 

 

 具体实现方案: session存的用户信息加一个判断条件  flag 每次登录时将flag设置为0 然后将用户的session信息记录到 一张session信息表中,在记录之前,先通过当前用户id查上一次用户登录的信息,然后用HTTPClient 在请求头里面塞上一次的sessionid 请求上一次的ip地址 将 fla改为1  然后拦截器 里用flag判断  当 flag里为1时 移除当前session

三、代码

1、LoginController

复制代码
package com.mangoubiubiu.controller;


import com.mangoubiubiu.annotation.Auth;
import com.mangoubiubiu.entities.HuasUser;
import com.mangoubiubiu.event.SessionIdEvent;
import com.mangoubiubiu.exception.BusinessException;
import com.mangoubiubiu.exception.CommonErrorCode;
import com.mangoubiubiu.service.LoginService;
import com.mangoubiubiu.utils.R;
import com.mangoubiubiu.vo.UserLogin;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;

@Api(tags = "登录模快")
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class LoginController {
    final ApplicationEventPublisher context;
    final LoginService loginService;

    @ApiImplicitParam(name = "userLogin",value = "用户信息",required = true)
    @ApiOperation(value = "登录接口")
    @PostMapping("/login")
    public R login(@RequestBody UserLogin userLogin){
        boolean flag = false;
        String type = userLogin.getType();
        if(StringUtils.isBlank(type)){
            throw  new BusinessException(CommonErrorCode.E_100101);
        }
        //账号密码登录
        if("0".equals(type)){
            flag= loginService.loginByPwd(userLogin);
        //验证码登录
        }else if("1".equals(type)){
            flag= loginService.loginByPhone(userLogin);

        }

        return flag==true?R.ok():R.error().message("登录失败");
   }

    @Auth
    @ApiImplicitParam(name = "userLogin",value = "查询用户信息",required = true)
    @ApiOperation(value = "查询用户信息")
    @GetMapping("/msg")
    public R mgs(){
        HuasUser user= loginService.getUserMsg();
        return R.ok().data("list",user);
    }

    /**
     * 查询用户信息
     * @return
     */
    @ApiOperation(value = "用户退出")
    @GetMapping("/logout")
    public R logout(){
        loginService.logout();
        return R.ok();
    }

    @ApiOperation(value = "设置用户登录标识")
    @GetMapping("/setUserStatus")
    public R setUserStatus(HttpServletRequest request){
        loginService.setUserStatus(request);
        return R.ok();
    }



}
复制代码

定义改变用户状态的方法

 2、LoginServiceImpl

复制代码
package com.mangoubiubiu.service.impl;

import com.mangoubiubiu.entities.HuasUser;
import com.mangoubiubiu.enums.SessionCodeEnum;
import com.mangoubiubiu.event.SessionIdEvent;
import com.mangoubiubiu.event.UserSingOutEvent;
import com.mangoubiubiu.exception.BusinessException;
import com.mangoubiubiu.exception.CommonErrorCode;
import com.mangoubiubiu.mapper.HuasUserMapper;
import com.mangoubiubiu.service.LoginService;
import com.mangoubiubiu.service.SessionService;
import com.mangoubiubiu.utils.PasswordUtil;
import com.mangoubiubiu.vo.UserLogin;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {

    final ApplicationEventPublisher context;

    final HuasUserMapper mapper;

    final SessionService sessionService;

    @Override
    public boolean loginByPwd(UserLogin userLogin) {
        String account = userLogin.getAccount();
        String pwd= userLogin.getPwd();

        if(StringUtils.isBlank(account) || StringUtils.isBlank(pwd)){
            throw new BusinessException(CommonErrorCode.E_100101);
        }
        HuasUser huasUser = mapper.selectByAccount(account);
        //用户不存在
        if(huasUser==null){
            throw new BusinessException(CommonErrorCode.U_900102);
        }
        System.out.println(huasUser.toString());
        //校验密码
        boolean verify = PasswordUtil.verify(pwd, huasUser.getPwd());
        if(!verify){
            throw new BusinessException(CommonErrorCode.U_900103);
        }
        //设置用户登录标识
        huasUser.setUserLogFlag("0");
        //保存session
        sessionService.saveSession(huasUser);
        //发布事件 跨机房单点签退
        context.publishEvent(new UserSingOutEvent(this));
        //发布事件 记录当前sessionid
        context.publishEvent( new SessionIdEvent(this));

        return true;
    }

    @Override
    public boolean loginByPhone(UserLogin userLogin) {
        return false;
    }

    @Override
    public HuasUser getUserMsg() {
        HuasUser  huasUser = (HuasUser) sessionService.getSessionByKey(SessionCodeEnum.USER_INFO.getKey());
        return huasUser;
    }

    @Override
    public void logout() {
        sessionService.removieSession();
    }

    @Override
    public void setUserStatus(HttpServletRequest request) {
        HuasUser huasUser = (HuasUser)sessionService.getSessionByKey(SessionCodeEnum.USER_INFO.getKey());
        if(huasUser!=null){
            huasUser.setUserLogFlag("1");
            sessionService.saveSession(huasUser);
        }
    }

    public static void main(String[] args) {
        System.out.println(PasswordUtil.generate("123456"));
    }

}
复制代码

 3、事件监听类

复制代码
package com.mangoubiubiu.event.listener;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mangoubiubiu.entities.HuasSessionid;
import com.mangoubiubiu.entities.HuasUser;
import com.mangoubiubiu.enums.SessionCodeEnum;
import com.mangoubiubiu.event.SessionIdEvent;
import com.mangoubiubiu.event.UserSingOutEvent;
import com.mangoubiubiu.mapper.HuasSessionidMapper;
import com.mangoubiubiu.service.SessionService;
import com.mangoubiubiu.utils.OkHttpUtil;
import com.mangoubiubiu.utils.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@RequiredArgsConstructor
@Slf4j
@Component
public class UserSingOutEventListener {

    final SessionService sessionService;

    final HuasSessionidMapper mapper;

    /**
     * 跨机房单点签退
     * @param event
     */
    @EventListener
    public void singOut(UserSingOutEvent event){
        HuasUser user=(HuasUser)sessionService.getSessionByKey(SessionCodeEnum.USER_INFO.getKey());

        HuasSessionid sessionid = mapper.selectByUserIdDesc(user.getId());
        if(sessionid!=null){
            String jessionid="JSESSIONID="+sessionid.getSessionId();
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            String localPort=sessionid.getPort();
            String localAddr=request.getLocalAddr();
            String url="http://"+localAddr+":"+localPort+"/huas/user/setUserStatus";
            JSONObject jsonObject=new JSONObject();
            System.out.println("----jessionid--"+jessionid);;
            try {
                JSONObject result = OkHttpUtil.get(url, jessionid);
                log.info("USER SIGNOUT----->{}",result.toString());
            }catch (Exception e){
                log.info(e.getMessage());
            }

        }
    }

}
复制代码

复制代码
package com.mangoubiubiu.event.listener;

import com.mangoubiubiu.entities.HuasSessionid;
import com.mangoubiubiu.entities.HuasUser;
import com.mangoubiubiu.enums.SessionCodeEnum;
import com.mangoubiubiu.event.SessionIdEvent;
import com.mangoubiubiu.mapper.HuasSessionidMapper;
import com.mangoubiubiu.service.SessionService;
import com.mangoubiubiu.utils.CodeNoUtil;
import com.mangoubiubiu.utils.CodePrefixCode;
import com.mangoubiubiu.utils.DateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;


@RequiredArgsConstructor
@Slf4j
@Component
public class SessionIdEventListener {

    final SessionService sessionService;

    final HuasSessionidMapper mapper;

    @EventListener
    public void insertSessionId(SessionIdEvent event){
        HuasUser user=(HuasUser)sessionService.getSessionByKey(SessionCodeEnum.USER_INFO.getKey());
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

        HuasSessionid huasSessionid=new HuasSessionid();
        String sessionid =sessionService.getSessionId();
        String userId=user.getId();
        huasSessionid.setUserId(user.getId());
        huasSessionid.setSessionId(sessionid);
        huasSessionid.setId(CodeNoUtil.getNo(CodePrefixCode.CODE_NO_PREFIX));
        huasSessionid.setAddr(request.getLocalAddr());
        huasSessionid.setPort(String.valueOf(request.getLocalPort()));
        huasSessionid.setCreateTime(DateUtil.format(new Date(),DateUtil.YYYYMMDDHHMMSS));
        huasSessionid.setUpdateTime(DateUtil.format(new Date(),DateUtil.YYYYMMDDHHMMSS));
        int count = mapper.selectSessionId(sessionid);
        if(count==0){
            mapper.insert(huasSessionid);
        }
    }


}
复制代码

 4、拦截器类

复制代码
package com.mangoubiubiu.intercepter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.mangoubiubiu.entities.HuasUser;
import com.mangoubiubiu.enums.SessionCodeEnum;
import com.mangoubiubiu.service.SessionService;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;



@Component
@RequiredArgsConstructor
public class LoginInterceptor implements HandlerInterceptor {

    final SessionService sessionService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HuasUser user=(HuasUser)request.getSession().getAttribute(SessionCodeEnum.USER_INFO.getKey());
        String servletPath = request.getServletPath();
        //如果当前请求页是登录页 跳转到首页
        if("/mvc/index".equals(servletPath) ){
            if (user!=null) {
                System.out.println("/mvc/index");
                request.getRequestDispatcher("/mvc/l/admin").forward(request, response);
            }
            return true;
        }
        //用户已登录
        if (user!=null) {
            //如果用户状态改变了 移除当前session
            if("1".equals(user.getUserLogFlag())){
                sessionService.removieSession();
            }
            //放行
            return true;
        }
        request.getRequestDispatcher("/mvc/index").forward(request, response);

        //重定向到登录页面
        return false;
    }
}
复制代码

四、测试

先有9000  和 10000 2个服务  在9000登录后 再在10000登录  9000 刷新 后回到登录页

 

 

 

 

 10000登录后拿到 sessionid 去请求 9000 让 9000去改当前用户的状态,此后  9000这边只有在有请求进入拦截器 就会把session清掉  。

刷新2次 就会退出,下图还没有请求  

第一次刷新 发现没有用户信息了  并且/user/msg 返回的是一个页面

因为拦截器拦截路径是  /mvc/**

这里进入admin被拦截了   移除掉了session

        registry.addInterceptor(interceptor).addPathPatterns("/mvc/**");

 再在页面请求 /user/msg 时  执行 Aop里的方法 取不到 session就跳页面

 

 第二次刷新  /huas/mvc/l/admin  进入到拦截器  从session里 取不到 用户信息 就转发到了登录页 

 

当然 登录 拦截器哪里可以直接写 移除session 后跳到登录页 就不用 刷新2次了 

 成功!!!!!!!!!!!!

 

本文作者:KwFruit

本文链接:https://www.cnblogs.com/mangoubiubiu/p/16102931.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   KwFruit  阅读(540)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起