SpringBoot集成单点登录-“被挤下线”
SpringBoot配置单点登录
前言
你好,未来!上个周末过的还行,逛街逛到腿发软,生活还是要有仪式感,一生要待自己待你最亲近的人。周一休息,顺便看了看04版天龙八部,塑造了三位英雄人物,共同点:热血男儿,助人为乐,乔峰大侠气概,段誉风流倜傥,虚竹严于律己,久久思考金庸在写的时候,思想与灵魂是何尝不是伟大,让我也陷入了沉思,久久不能自拔。
中言
前言总会自己乱说一通,然后才能进入主题一些相关而有不相关的东西,都能说一些,可能一个人久了,发现就慢慢的变成了另一个人,好吧,就此打住,还是说说对我们有用的东西吧!
单点登录:顾名思义,就是只能一个人登录
思路:在登录时候传入username和lastLoginDate,生成token时,验证每次登录的时候过滤器判断最后登录时间,如果一直通行,不一致时候,直接返回error
代码实现:
登录
public UserTokenState login(String username, String password, Device device) {try{final Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username,password));// Inject into security contextSecurityContextHolder.getContext().setAuthentication(authentication);JwtUser jwtuser = (JwtUser)authentication.getPrincipal();return generateUserTokenState(jwtuser.getId(),jwtuser.getUsername(),device);}catch (Exception e){logger.error("用户登录异常{}",e.getMessage());return null;}}
生成用户token
private UserTokenState generateUserTokenState(Long userId,String username,Device device){// token creationfinal String timestamp = TimeProvider.getTimestampNow().toString();com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();jsonObject.put("username",username);jsonObject.put("lastLoginDate",timestamp);String jws = jwtUtil.generateToken( jsonObject, device);int expiresIn = jwtUtil.getExpiredIn(device);User user = userRepository.findByUsername(username);user.setLastLoginDate(timestamp);userRepository.save(user);//登出其他登录,并记录登录信息try{//后期在解释,此处处理单点登录踢出提示(类似,QQ你有新设备登录,您已经处于离线状态)sendLogOutMessage(userId);}catch (Exception e){e.printStackTrace();}return new UserTokenState(userId,jws,expiresIn);}
生成token
/*** 通过用户名与设备类型生成token* @param userinfo* @param device* @return*/public String generateToken(JSONObject userinfo, Device device) {String audience = generateAudience(device);return Jwts.builder().setIssuer( APP_NAME ).setSubject(userinfo.toString()).setAudience(audience).setIssuedAt(timeProvider.now()).setExpiration(generateExpirationDate(device)).signWith( SIGNATURE_ALGORITHM, SECRET ).compact();}
根据设备类型进行token过期设定
/*** 根据设备类型进行token过期时间的设定(可以自己设定)* @param device* @return 转秒*/private Date generateExpirationDate(Device device) {long expiresIn = device.isTablet() || device.isMobile() ? MOBILE_EXPIRES_IN : EXPIRES_IN;return new Date(timeProvider.now().getTime() + expiresIn * 1000);}
封装过滤器
@Overridepublic void doFilterInternal(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,FilterChain chain) throws IOException, ServletException {ServletRequest requestWrapper = null;HttpServletRequest request= (HttpServletRequest) httpServletRequest;HttpServletResponse response= (HttpServletResponse) httpServletResponse;//response此处自己封装自己用的header文件String username;logger.info("doFilterInternal={}",request.getHeader("Authorization"));try {//获取通用参数BaseRequestParam baseRequestParam = parseRequestParam(request);request.setAttribute("baseRequestParam",baseRequestParam);String authToken = jwtUtil.getToken(request);if (authToken != null) {// get username from tokenusername = jwtUtil.getUsernameFromToken(authToken);logger.info("login username: "+username);if (username != null) {// get userUserDetails userDetails = userDetailsService.loadUserByUsername(username);if (jwtUtil.validateToken(authToken, userDetails)) {// create authenticationTokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails);authentication.setToken(authToken);SecurityContextHolder.getContext().setAuthentication(authentication);} else {throw new ClassOnlineException(HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");}}else {throw new ClassOnlineException(HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");}}}catch (Exception e) {response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());return;}//此处为了获取一些基础参数,为了日志或者后期实用方便写下的if(request instanceof HttpServletRequest) { //判断是否是http请求requestWrapper = new MAPIHttpServletRequestWrapper((HttpServletRequest) httpServletRequest); //再封装requestString json = HttpHelper.getBodyString(requestWrapper);logger.info("传递参数解析 => {}"+json);//获取请求参数Map<String, String[]> map = requestWrapper.getParameterMap();logger.info("过滤器过滤的参数filter => " + httpServletRequest.getRemoteHost() + " " + JSONObject.toJSONString(map));}if(requestWrapper == null) {chain.doFilter(request, response);} else {try {chain.doFilter(requestWrapper, response);} catch (ServletException e) {logger.error("doFilterInternal method: filter is exception" + e.getStackTrace().toString());chain.doFilter(request, response);} catch (IOException e){logger.error("doFilterInternal method: filter is exception" + e.getStackTrace().toString());chain.doFilter(request, response);}}}
上述基本上一个单点流程走完了,但是接下来或许你可以参考
单点登录“被挤下线”
所谓的“被挤下线”功能,即一个账号在A客户端保持登录状态,然后又在B客户端进行登录操作,那么A客户端就会被挤下线
App如何知道该账户已经在其他设备上登陆了呢?有三种实现方式
- api请求中后台返回特定的code。缺点是需要下次请求才知道被踢下线
- 使用推送。后台可以推送给APP,从而使APP得知已在其他地方登陆,可以及时响应。
- 使用第三方的监听器。比如集成了环信,环信自身有提供连接状态的接听,通过监听环信的用户状态,从而达到监听app自身用户系统的效果
环信的即时聊天:
Android端:
// 注册连接监听 全局的监听
EMChatManager.getInstance().addConnectionListener(connectionListener);
实现这个链接监听
连接监听,的那个检测到连接断开的时候判断是用户被移除还是连接冲突即账号在其他地方登陆,做出相应的操作。
connectionListener = new EMConnectionListener() {
@Override
public void onDisconnected(int error) {
if (error == EMError.USER_REMOVED) {
onCurrentAccountRemoved();
} else if (error == EMError.CONNECTION_CONFLICT) {
onConnectionConflict();
}
}
@Override
public void onConnected() {
// in case group and contact were already synced, we supposed to
// notify sdk we are ready to receive the events
}
};
我们只关心账号在别处登陆,这个时候,我们一般要跳转到MainActivity,然后强制弹出对话框提示用户重新登陆。
/**
- 账号在别的设备登录
*/
protected void onConnectionConflict() {
Intent intent = new Intent(appContext, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constant.ACCOUNT_CONFLICT, true);
appContext.startActivity(intent);
}
这个地方检测到登陆冲突之后需要回到MainActivity,并为MainActivity携带了一个标识和一个标记位Intent.FLAG_ACTIVITY_NEW_TASK,表示在一个新的task中开启一个Activity,如果包含这个Activity的task已经在运行,那么这个Activity就回到前台显示。然后回调onNewIntent()方法处理这个Intent。
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.getBooleanExtra(Constant.ACCOUNT_CONFLICT, false) && !isConflictDialogShow) {
showConflictDialog();
} else if (intent.getBooleanExtra(Constant.ACCOUNT_REMOVED, false)
&& !isAccountRemovedDialogShow) {
showAccountRemovedDialog();
}
}
首先会判断标识,如果是账户冲突就会弹出对话框提示用户跳转登陆页面重新登陆。另外这个对话框是不能取消也不可关闭的。
这样被挤下线功能就基本实现了。
踢出用户提示
消息踢出:实现方式,其实单点登录到用户下线都已经成型了,接下来就是优化和完善人为提示,可有可无,看你做到什么程度
思路:用阿里到消息队列发送一条push,app和ios接受到之后,做出相应到提示
实现:logout方式踢出
/*** 用户被登出时发送消息*/private void sendLogOutMessage(Long userId){//获取当前用户登录的信息Object loginInfoObj = cacheService.get("LOGIN_INFO" + userId);if(loginInfoObj != null){JSONObject userJson = JSONObject.fromObject(loginInfoObj);String pushType = StringUtils.isBlank(userJson.getString(CommonConsts.PUSH_TYPE)) ? "":userJson.getString(CommonConsts.PUSH_TYPE);String pushId = StringUtils.isBlank(userJson.getString(CommonConsts.PUSH_ID)) ? "":userJson.getString(CommonConsts.PUSH_ID);if(!StringUtils.equals("-1",pushType)){//将消息存储到消息队列中JSONObject requestBodyJson = new JSONObject();requestBodyJson.put(CommonConsts.PUSH_MSG_TYPE, CommonConsts.PUSH_MSG_TYPE_LOGOUT);requestBodyJson.put(CommonConsts.PUSH_TYPE,pushType);requestBodyJson.put(CommonConsts.PUSH_ID,pushId);aliMQServiceImpl.sendMessage(CommonConsts.MQPRODUCER_TYPE_PUSH,aliMQServiceImpl.getTopicPush(),aliMQServiceImpl.getTagPush(),requestBodyJson.toString().getBytes());}}}
后序
用一句话结束今天的记录吧,生命不止,奋斗不息。
SpringBoot集成单点登录-“被挤下线”相关推荐
- Springboot集成社交登录功能(微博登录)以及Session共享
Springboot集成社交登录功能 pom <dependency><groupId>org.apache.httpcomponents</groupId>< ...
- SpringBoot 集成第三方登录(微信、支付宝)
SpringBoot 集成第三方登录 微信 1. 登录微信开发平台 1.在微信开发平台里面获取我们需要的AppID.AppSecret . 2.创建配置类 3. 开始自己的业务模块 1. 方便测试 支 ...
- 禅道与企业微信集成单点登录
背景 公司使用企业微信做为办公管理系统.为了公司人事相关研发系统统一授权认证,实现业务系统账号与企业微信挂钩,做到统一管理授权,以此才有了禅道与企业微信集成单点登录研究. 集成步骤 集成步骤大致分为三 ...
- springboot+cas单点登录
参考java1234.com 一. CAS介绍 简介:CAS是Central Authentication Service的缩写,中央认证服务,一种独立开放指令协议.CAS 是 耶鲁大学(Yale U ...
- java 账户挤下线提示_运用session来控制用户的异地登录被挤下线情况
在用QQ的过程中我们如果你的账号在另外一台手机上面登录,这是腾讯后台会提醒你异地登录,可能你的账号被盗了,然后你手机上得QQ就会被退出登录,这个时候你就需要重新登录修改密码,以确保账号的安全.那这种被 ...
- springboot实现单点登录_什么是单点登录,php是如何实现单点登录的
文章来自:php中文网链接:https://www.php.cn/php-weizijiaocheng-429869.html 作者:中文网 商务合作:请加微信(QQ):2230304070 视频教程 ...
- 架构 - 单点登录 - Springboot 模拟单点登录
SSO: Single Sign On,官方的概念:web系统由单系统发展成多系统组成的应用群,复杂性应该由系统内部承担,而不是用户.无论web系统内部多么复杂,对用户而言,都是一个统一的整体,也就是 ...
- SpringBoot模拟单点登录
SSO: Single Sign On,官方的概念:web系统由单系统发展成多系统组成的应用群,复杂性应该由系统内部承担,而不是用户.无论web系统内部多么复杂,对用户而言,都是一个统一的整体,也就是 ...
- SMAL2.0集成单点登录(SAP-SF)
一.什么是 SAML 协议? SAML 即安全断言标记语言,英文全称是 Security Assertion Markup Language.它是一个基于 XML 的标准,用于在不同的安全域(secu ...
最新文章
- TextField输入结束后让键盘消失的两个技巧
- python中读取文件过程中seek()函数的使用
- gitlab更新配置无效_GitMaster 发布 v1.11.0 版本,支持 GitLab 多级分组,Gist支持文件列表...
- OpenCV图像处理——深度学习样本制造
- Linux学习笔记之安装mplayer过程详解
- java 接口的作用和好处
- linux备份目录命令tar,Tar命令备份还原Linux系统
- 阿里云安全肖力:云的六大安全基因助力企业构建智能化安全体系
- chm文件的中文显示乱码问题解决
- java Monitor对象监视器、对象头、mark word
- 计算机开机显示花屏,win7电脑开机过程中出现花屏怎么办
- 用金山打字通练习打字
- flexPaper制作在线文库阅读器思路
- 小程序之100推荐:901~1000
- 市场调研-全球与中国汽车零部件涂层市场现状及未来发展趋势
- AutoJs学习-实现2048游戏机
- python3 子进程和父进程
- 《实用C++》第11课:if 语句实现逻辑运算与冒号表达式
- Java---从键盘输入一位整数,当输入1-7时,输出星期一~星期日
- Java基础题10:(单选题)以下代码的输出结果是() public class Test { public static void main(String[] args) { Stri
热门文章
- 本宝宝blog的背景图片。。
- 数字理想助力500强央企展示世界一流企业风范
- 服务器、存储、数据保护……又把奖拿了个遍
- MathType Commands for Microsoft Word ErrorXThe MathType commands could not communicate with MathType
- 用CHARIOT测量网络带宽、网速
- Maven工程下,解决配置文件相关的File doesn't exits,以及xxxMapper.xml doesn't exits
- 《我的一个 OIer 朋友》歌词
- 超级详细 的 Redis 安装教程
- ownCloud简介
- (CVE-2020-9483)ApacheSkyWalking SQL注入 漏洞复现