最新要闻
- 【世界新要闻】2799元配酷睿i5!小米迷你主机将开启众筹 便宜900元条件公布
- 环球热门:小米13黑色版图赏:直屏直边手感秒杀iPhone 14!
- 真爱粉!有米粉花近万元抢了两台小米13
- 世界快资讯:好评率超99% 小米MIX Fold 2推出月光银配色 雷军:镜面效果令人惊叹
- 天天热点评!比RTX 4090便宜5000 AMD RX 7900系列显卡年底供货20万块:备货充足
- 小米13系列供不应求!线上已售罄 3999元起
- 环球即时:差价1000元!一文了解小米13和13 Pro区别
- 世界观察:NASA猎户座登月飞船成功返回地球:美国完成重返月球第一步
- 环球观天下!手机“毒瘤”微信终于修复!转发文件多次保存问题解决:不占空间了
- 天天微头条丨算了吧!电脑RGB光效好看不好玩
- 59岁李连杰晒照引网友围观 “功夫皇帝”定居国外略显苍老
- 穷人入侵TikTok直播间:躺着就能赚钱 真爽
- 焦点热讯:为了帮梅西赢球 我敲了一晚上的电子木鱼
- 环球速递!2022贺岁档总票房突破4亿!《阿凡达2》势不可挡 最快破亿
- 快播:雷军运气爆棚:在小米手机官方活动 中奖一台小米13
- 世界资讯:小米音质最好智能音箱!小米Sound Pro开箱图赏
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
【当前独家】公司产品太多了,怎么实现一次登录产品互通?
作者:王老狮 来源:juejin.cn/post/7123787027652280356
最近开发新产品,然后老板说我们现在系统太多了,每次切换系统登录太麻烦了,能不能做个优化,同一账号互通掉。作为一个资深架构狮,老板的要求肯定要满足,安排!
(相关资料图)
一个公司产品矩阵比较丰富的时候,用户在不同系统之间来回切换,固然对产品用户体验上较差,并且增加用户密码管理成本。也没有很好地利用内部流量进行用户打通,并且每个产品的独立体系会导致产品安全度下降。
因此实现集团产品的单点登录对用户使用体验以及效率提升有很大的帮助。那么如何实现统一认证呢?我们先了解一下传统的身份验证方式。
传统Session机制及身份认证方案
Cookie与服务器的交互
众所周知,http是无状态的协议,因此客户每次通过浏览器访问web页面,请求到服务端时,服务器都会新建线程,打开新的会话,而且服务器也不会自动维护客户的上下文信息。比如我们现在要实现一个电商内的购物车功能,要怎么才能知道哪些购物车请求对应的是来自同一个客户的请求呢?
因此出现了session这个概念,session 就是一种保存上下文信息的机制,他是面向用户的,每一个SessionID 对应着一个用户,并且保存在服务端中。session主要 以 cookie 或 URL 重写为基础的来实现的,默认使用 cookie 来实现,系统会创造一个名为JSESSIONID的变量输出到cookie中。
JSESSIONID 是存储于浏览器内存中的,并不是写到硬盘上的,如果我们把浏览器的cookie 禁止,则 web 服务器会采用 URL 重写的方式传递 Sessionid,我们就可以在地址栏看到 sessionid=KWJHUG6JJM65HS2K6 之类的字符串。
通常 JSESSIONID 是不能跨窗口使用的,当你新开了一个浏览器窗口进入相同页面时,系统会赋予你一个新的sessionid,这样我们信息共享的目的就达不到了。
服务器端的session的机制
当服务端收到客户端的请求时候,首先判断请求里是否包含了JSESSIONID的sessionId,如果存在说明已经创建过了,直接从内存中拿出来使用,如果查询不到,说明是无效的。
如果客户请求不包含sessionid,则为此客户创建一个session并且生成一个与此session相关联的sessionid,这个sessionid将在本次响应中返回给客户端保存。
对每次http请求,都经历以下步骤处理:
- 服务端首先查找对应的cookie的值(sessionid)。
- 根据sessionid,从服务器端session存储中获取对应id的session数据,进行返回。
- 如果找不到sessionid,服务器端就创建session,生成sessionid对应的cookie,写入到响应头中。
session是由服务端生成的,并且以散列表的形式保存在内存中
基于 session 的身份认证流程
基于seesion的身份认证主要流程如下:
因为 http 请求是无状态请求,所以在 Web 领域,大部分都是通过这种方式解决。但是这么做有什么问题呢?我们接着看
集群环境下的 Session 困境及解决方案
随着技术的发展,用户流量增大,单个服务器已经不能满足系统的需要了,分布式架构开始流行。通常都会把系统部署在多台服务器上,通过负载均衡把请求分发到其中的一台服务器上,这样很可能同一个用户的请求被分发到不同的服务器上,因为 session 是保存在服务器上的,那么很有可能第一次请求访问的 A 服务器,创建了 session,但是第二次访问到了 B 服务器,这时就会出现取不到 session 的情况。
我们知道,Session 一般是用来存会话全局的用户信息(不仅仅是登陆方面的问题),用来简化/加速后续的业务请求。传统的 session 由服务器端生成并存储,当应用进行分布式集群部署的时候,如何保证不同服务器上 session 信息能够共享呢?
Session共享方案
Session共享一般有两种思路
- session复制
- session集中存储
session复制
session复制即将不同服务器上 session 数据进行复制,用户登录,修改,注销时,将session信息同时也复制到其他机器上面去。
这种实现的问题就是实现成本高,维护难度大,并且会存在延迟登问题。
session集中存储
集中存储就是将获取session单独放在一个服务中进行存储,所有获取session的统一来这个服务中去取。这样就避免了同步和维护多套session的问题。一般我们都是使用redis进行集中式存储session。
多服务下的登陆困境及SSO方案
SSO的产生背景
如果企业做大了之后,一般都有很多的业务支持系统为其提供相应的管理和 IT 服务,按照传统的验证方式访问多系统,每个单独的系统都会有自己的安全体系和身份认证系统。进入每个系统都需要进行登录,获取session,再通过session访问对应系统资源。这样的局面不仅给管理上带来了很大的困难,对客户来说也极不友好,那么如何让客户只需登陆一次,就可以进入多个系统,而不需要重新登录呢?
“单点登录”就是专为解决此类问题的。其大致思想流程如下:通过一个 ticket 进行串接各系统间的用户信息。
SSO的底层原理 CAS
CAS实现单点登录流程
我们知道对于完全不同域名的系统,cookie 是无法跨域名共享的,因此 sessionId 在页面端也无法共享,因此需要实现单店登录,就需要启用一个专门用来登录的域名如(ouath.com)来提供所有系统的sessionId。
另外,最近面试整理了 Java 最新、最全的面试题:
https://www.javastack.cn/mst/
当业务系统被打开时,借助中心授权系统进行登录,整体流程如下:
- 当b.com打开时,发现自己未登陆,于是跳转到ouath.com去登陆
- ouath.com登陆页面被打开,用户输入帐户/密码登陆成功
- ouath.com登陆成功,种 cookie 到ouath.com域名下
- 把 sessionid 放入后台redis,存放
数据结构,然后页面重定向到A系统 - 当b.com重新被打开,发现仍然是未登陆,但是有了一个 ticket值
- 当b.com用ticket 值,到 redis 里查到 sessionid,并做 session 同步,然后种cookie给自己,页面原地重定向
- 当b.com打开自己页面,此时有了 cookie,后台校验登陆状态,成功
整个交互流程图如下:
单点登录流程演示
CAS登录服务demo核心代码
用户实体类
public class UserForm implements Serializable{ private static final long serialVersionUID = 1L; private String username; private String password; private String backurl; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getBackurl() { return backurl; } public void setBackurl(String backurl) { this.backurl = backurl; }}
登录控制器
@Controllerpublic class IndexController { @Autowired private RedisTemplate redisTemplate; @GetMapping("/toLogin") public String toLogin(Model model,HttpServletRequest request) { Object userInfo = request.getSession().getAttribute(LoginFilter.USER_INFO); //不为空,则是已登陆状态 if (null != userInfo){ String ticket = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(ticket,userInfo,2, TimeUnit.SECONDS); return "redirect:"+request.getParameter("url")+"?ticket="+ticket; } UserForm user = new UserForm(); user.setUsername("laowang"); user.setPassword("laowang"); user.setBackurl(request.getParameter("url")); model.addAttribute("user", user); return "login"; } @PostMapping("/login") public void login(@ModelAttribute UserForm user,HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException { System.out.println("backurl:"+user.getBackurl()); request.getSession().setAttribute(LoginFilter.USER_INFO,user); //登陆成功,创建用户信息票据 String ticket = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(ticket,user,20, TimeUnit.SECONDS); //重定向,回原url ---a.com if (null == user.getBackurl() || user.getBackurl().length()==0){ response.sendRedirect("/index"); } else { response.sendRedirect(user.getBackurl()+"?ticket="+ticket); } } @GetMapping("/index") public ModelAndView index(HttpServletRequest request) { ModelAndView modelAndView = new ModelAndView(); Object user = request.getSession().getAttribute(LoginFilter.USER_INFO); UserForm userInfo = (UserForm) user; modelAndView.setViewName("index"); modelAndView.addObject("user", userInfo); request.getSession().setAttribute("test","123"); return modelAndView; }}
登录过滤器
public class LoginFilter implements Filter { public static final String USER_INFO = "user"; @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse)servletResponse; Object userInfo = request.getSession().getAttribute(USER_INFO);; //如果未登陆,则拒绝请求,转向登陆页面 String requestUrl = request.getServletPath(); if (!"/toLogin".equals(requestUrl)//不是登陆页面 && !requestUrl.startsWith("/login")//不是去登陆 && null == userInfo) {//不是登陆状态 request.getRequestDispatcher("/toLogin").forward(request,response); return ; } filterChain.doFilter(request,servletResponse); } @Override public void destroy() { }}
配置过滤器
@Configurationpublic class LoginConfig {//配置filter生效@Beanpublic FilterRegistrationBean sessionFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new LoginFilter()); registration.addUrlPatterns("/*"); registration.addInitParameter("paramName", "paramValue"); registration.setName("sessionFilter"); registration.setOrder(1); return registration;}}
登录页面
enjoy login 请登陆
推荐一个开源免费的 Spring Boot 最全教程:
https://github.com/javastacks/spring-boot-best-practice
web系统demo核心代码
过滤器
public class SSOFilter implements Filter { private RedisTemplate redisTemplate; public static final String USER_INFO = "user"; public SSOFilter(RedisTemplate redisTemplate){ this.redisTemplate = redisTemplate; } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse)servletResponse; Object userInfo = request.getSession().getAttribute(USER_INFO);; //如果未登陆,则拒绝请求,转向登陆页面 String requestUrl = request.getServletPath(); if (!"/toLogin".equals(requestUrl)//不是登陆页面 && !requestUrl.startsWith("/login")//不是去登陆 && null == userInfo) {//不是登陆状态 String ticket = request.getParameter("ticket"); //有票据,则使用票据去尝试拿取用户信息 if (null != ticket){ userInfo = redisTemplate.opsForValue().get(ticket); } //无法得到用户信息,则去登陆页面 if (null == userInfo){ response.sendRedirect("http://127.0.0.1:8080/toLogin?url="+request.getRequestURL().toString()); return ; } /** * 将用户信息,加载进session中 */ UserForm user = (UserForm) userInfo; request.getSession().setAttribute(SSOFilter.USER_INFO,user); redisTemplate.delete(ticket); } filterChain.doFilter(request,servletResponse); } @Override public void destroy() { }}
控制器
@Controllerpublic class IndexController { @Autowired private RedisTemplate redisTemplate; @GetMapping("/index") public ModelAndView index(HttpServletRequest request) { ModelAndView modelAndView = new ModelAndView(); Object userInfo = request.getSession().getAttribute(SSOFilter.USER_INFO); UserForm user = (UserForm) userInfo; modelAndView.setViewName("index"); modelAndView.addObject("user", user); request.getSession().setAttribute("test","123"); return modelAndView; }}
首页
enjoy index cas-website:欢迎你">
CAS的单点登录和OAuth2的区别
- OAuth2: 三方授权协议,允许用户在不提供账号密码的情况下,通过信任的应用进行授权,使其客户端可以访问权限范围内的资源。
- CAS: 中央认证服务(Central Authentication Service),一个基于Kerberos票据方式实现SSO单点登录的框架,为Web 应用系统提供一种可靠的单点登录解决方法(属于 Web SSO )。
- CAS的单点登录时保障客户端的用户资源的安全 ;OAuth2则是保障服务端的用户资源的安全 。
- CAS客户端要获取的最终信息是,这个用户到底有没有权限访问我(CAS客户端)的资源;OAuth2获取的最终信息是,我(oauth2服务提供方)的用户的资源到底能不能让你(oauth2的客户端)访问。
因此,需要统一的账号密码进行身份认证,用CAS;需要授权第三方服务使用我方资源,使用OAuth2。
关于 OAuth2、JWT 的最新应用,可以报名栈长的《Spring Cloud 微服务实战课》,一次订阅,永久免费学习。
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!
【当前独家】公司产品太多了,怎么实现一次登录产品互通?
【世界新要闻】2799元配酷睿i5!小米迷你主机将开启众筹 便宜900元条件公布
环球热门:小米13黑色版图赏:直屏直边手感秒杀iPhone 14!
真爱粉!有米粉花近万元抢了两台小米13
世界快资讯:好评率超99% 小米MIX Fold 2推出月光银配色 雷军:镜面效果令人惊叹
天天热点评!比RTX 4090便宜5000 AMD RX 7900系列显卡年底供货20万块:备货充足
小米13系列供不应求!线上已售罄 3999元起
环球即时:差价1000元!一文了解小米13和13 Pro区别
世界观察:NASA猎户座登月飞船成功返回地球:美国完成重返月球第一步
环球观天下!手机“毒瘤”微信终于修复!转发文件多次保存问题解决:不占空间了
天天微头条丨算了吧!电脑RGB光效好看不好玩
59岁李连杰晒照引网友围观 “功夫皇帝”定居国外略显苍老
世界看热讯:Git从入门到实战
Python 基于xml.etree.ElementTree实现XML对比
天天微资讯!飞链云智能机器人-基于ChatGPT的有趣问答
穷人入侵TikTok直播间:躺着就能赚钱 真爽
焦点热讯:为了帮梅西赢球 我敲了一晚上的电子木鱼
环球速递!2022贺岁档总票房突破4亿!《阿凡达2》势不可挡 最快破亿
GPU CPU向量加法时间测试
快播:雷军运气爆棚:在小米手机官方活动 中奖一台小米13
世界资讯:小米音质最好智能音箱!小米Sound Pro开箱图赏
Windows OpenGL 图像透明度调节
焦点热文:异步批处理教程
小米首款万兆路由发布:1799元
天天速递!只要699元!小米旗舰降噪耳机Buds 4发布:HiFi音质
小米8款新品价格汇总:3999元的小米13爱不爱?
全球新资讯:雷军:只有两家做双尺寸双高端 一家是苹果 一家是小米
小米13正式亮相:金属直边 继续对标iPhone!
今亮点!3999元起!小米13正式发布:黄金尺寸小屏、满血徕卡镜头
不涨价 小米13价格良心了:顶配12+512GB还便宜200元
全球微动态丨小米13 Pro正式亮相:中框仅仅3.2毫米!科技纳米皮手感无敌
焦点消息!ELK+FileBeat日志分析系统
快看点丨差错控制
全球信息:Intel显卡驱动即将大更新!DX9、DX11老游戏有惊喜
环球快资讯:MIUI 14“剃刀计划”砍向应用臃肿:微信文件重复存储问题终于解决
全球快看点丨全新界面 系统大幅精简!MIUI 14正式发布
【世界聚看点】使webworker中支持使用import导入模块——threads.js
世界速看:说人话系列:DOM(Document Object Mode)究竟是怎么来的呢?
环球快报:俞敏洪称不敢进高档美发店:一顿好酒肉没有了 心疼
当前热点-达尔文错了?螃蟹竟在地球上出现了5次
全球新资讯:时间轮 (史上最全)
【天天聚看点】雷军晒小米13生产过程 由小米智能工厂全自动化装配
百事通!Intel独立显卡不做了?掌门人一句话 放心了!
长安深蓝SL03宣布涨价:预计上调2000元-8000元
环球快讯:沉船捞出165年前牛仔裤80万卖出:更有数不清的黄金
SpringMVC执行流程
全球快资讯丨任务调度系统-业务线资源隔离
天天微速讯:echarts设置暂无数据
老版本微博下线倒计时!新版已无法返回老版本
舒适降噪的天花板 索尼WH-1000XM5耳机跌破2000元
全球视点!苹果把果链撤出中国?难度可是地狱级的
天天滚动:SQLyog连接MYSQL时报错 Client does not support authentication protocol requested
世界快报:小米发布米家智能小厨宝7L S1:2000W速热、1年省电100度
播报:男子将手机改装成“偷拍神器”:摄像头改到耳机插孔 赚了30万
世界资讯:我迟早被这些AI绘画笑死
世界快报:买不到抗原检测试剂盒怎么办?不要慌
重点聚焦!Intel A750显卡新驱动性能实测:《CS:GO》帧数飙升79%
公众号附件怎么添加?还在用阅读原文的方式来添加吗,你已经落伍啦,现在大家都在用这个新方法,不仅方便还省事!
TabControl控件的简单使用-添加tab
【全球时快讯】Python爬虫实战,requests+xlwt模块,爬取螺蛳粉商品数据(附源码)
环球微速讯:Linux笔记03: Linux常用命令_3.3文件操作命令
新资讯:canvas分享
全球今头条!推特重推蓝V认证服务 苹果用户得多交3美元!马斯克也无能为力
每日焦点!三星S23 Ultra关键参数确定:首发高频版第二代骁龙8、祖传25W快充
童年记忆的黄桃罐头为啥突然火了?专家建议不要盲目囤
世界今头条!【2023最新B站评论爬虫】用python爬取上千条哔哩哔哩评论
【世界快播报】刘强东称新冠比感冒还轻微!王石也发声:只有鼻子不舒服 10天转阴
全球今亮点!再见C罗!C罗赛后痛哭:葡萄牙0-1摩洛哥 非洲球队首进4强
世界杯英法大战结束!法国2-1淘汰英格兰 晋级四强
中国市场已合资30年!又一车企资不抵债:销量暴跌
全球观速讯丨中国车企在俄罗斯销量飙升:市占率超1/3 主打中高端
每日消息!uniapp+unicloud开发微信小程序流程
焦点快看:清理备用内存 - 解决方案(备用内存占用过高且不自动释放)--九五小庞
天天日报丨Blazor和Vue对比学习(进阶.路由导航四):路由传参
王冰冰反向操作:戴摩洛哥围巾 希望葡萄牙获胜
今日看点:老黄怎么看?这张RTX 4090卖到了10万块 有钱也不一定买到:EVGA绝版货
东北市民网购宠物猪收到已冻僵 网友唏嘘:一个敢买一个敢发
【环球新要闻】从 695. 岛屿的最大面积 入手深度优先搜素DFS
快资讯丨用户重复注册分析-多线程事务中加锁引发的bug
【焦点热闻】PTA作业6-8电信系列总结
环球要闻:C#中的WebAPI
专为AR/VR打造!苹果全新操作系统名为xrOS:有专属应用商店 最早明年推出
全球看点:高校花2.4亿把教学课堂搬到了海上 网友羡慕不已
2022-6.824-Lab2:Raft
007爬虫之requests模块进阶
全球今日讯!攻防世界-unseping(序列化,Bash shell)
全球观速讯丨《暗黑破坏神4》赛季通行证需氪金购买 暴雪重申:不会充钱就变强
JavaScript:this指针
20点狂欢:淘宝每满200减30、天猫/京东每满300减40
厂家称黄桃罐头没药效 网友调侃:黄桃罐头为何成东北人疯狂膜拜的神物
全球看点:验证jenkins自动化部署
世界速读:首个进入太空的人类 宇航员加加林个人档案已解密:34岁死于空难
天天即时看!女生高铁录乐器考试被打断:工作人员知道情况后特意给换了地方
2021冬--简单描述时间复杂度
PDF转图片
Python实战案例,tkinter+random模块,实现课堂随机抽选提问并语音播报学生姓名
世界关注:将近30万人预约!有米粉要当小米13 Pro钉子户
每日观察!86寸4K巨屏!小米EA Pro 86电视今日开售 5999元
比Zen4还火 AMD游戏神U锐龙7 5800X3D史低2199元(首发3099)
MIUI 14花宠摆件上线!米粉:小时候的QQ农场又回来了