开发者技术前线 ,汇集技术前线快讯和关注行业趋势,大厂干货,是开发者经历和成长的优秀指南。
更新时间:2024-05-10 16:16:07作者:佚名
Web 应用程序使用 / 和 http 作为通信协议。 HTTP 是一种无状态协议。 来自浏览器的每个请求都将由服务器独立处理,并且不会与之前或后续的请求关联。 这个过程如下图所示。 三个请求/响应对之间没有任何联系。
但这也意味着任何用户都可以通过浏览器访问服务器资源。 如果要保护服务器的某些资源,就必须限制浏览器请求; 限制浏览器请求,必须识别浏览器请求,响应合法请求,忽略非法请求; 为了识别浏览器请求,必须知道浏览器请求状态。 既然http协议是无状态的,那就让服务器和浏览器共同维护一个状态吧!这就是会话机制
2. 会话机制
浏览器第一次请求服务器时,服务器会创建一个会话并将会话 ID 作为响应的一部分发送到浏览器。 浏览器存储会话 ID 并在后续的第二个和第三个请求中携带它。 服务器通过获取请求中的 ID来判断是否是同一个用户。 这个过程如下图所示。 后续请求与第一个请求相关。
服务器将会话对象保存在内存中。 浏览器如何保存 id? 你可能会想到两种方法:
1、请求参数;
2、.
使用 id作为每个请求的参数,服务器在收到请求时自然可以解析参数得到 id,并以此来判断是否来自同一个。 显然,这种方法并不可靠。 然后让浏览器自己维护 id。 每次发送 http 请求时,浏览器都会自动发送会话 ID。 该机制就是用来做到这一点的。它是浏览器用来存储少量数据的机制。 数据以“键/值”的形式存储。 浏览器在发送http请求时自动附加信息。
当然,机制也已经实现了。 访问服务器时,可以在浏览器中看到一个名称“”。 这是会话机制维护的会话ID。 使用的请求响应流程如下:
3. 登录状态
通过会话机制,登录状态一目了然。 我们假设浏览器第一次请求服务器时,需要输入用户名和密码来验证身份。 服务器获取用户名和密码并在数据库中进行比较。 如果正确,则表示当前正在举行会议。 用户是合法用户,这个应该被标记为“已授权”或者“已登录”等,既然是的状态,自然需要保存在对象中。 在对象中设置登录状态如下
HttpSession session = request.getSession();
session.setAttribute("isLogin", true);
当用户再次访问时,在对象中查看登录状态
HttpSession session = request.getSession();
session.getAttribute("isLogin");
实现登录状态的浏览器请求服务器模型如下图所示
登录机制是通过每次请求受保护资源时检查对象中的登录状态来实现的,只有=true的才能被访问。
02多系统的复杂性
Web系统已经从古代的单一系统发展到由多个系统组成的应用程序群。 面对如此多的系统,用户是否必须一一登录,然后一一退出?如下图所示
Web系统已经从单一的系统发展到由多个系统组成的应用群。 复杂性应该由系统本身来承担,而不是由用户来承担。Web系统无论内部多么复杂,对于用户来说都是一个统一的整体。 也就是说,用户访问Web系统的整个应用组与访问单个系统是一样的。 只需一次登录/注销就足够了。
单系统登录方案虽然很完美,但已经不再适合多系统应用群体。 为什么?
单系统登录方案的核心是携带 ID来维持浏览器和服务器之间的会话状态。但也有限制。 这个限制就是域名(通常对应网站的域名)。 浏览器发送http请求时,会自动携带与匹配的域名,并非全部
既然如此,何不将Web应用组中所有子系统的域名统一到一个顶级域名下verify什么意思,比如“*.”,然后将它们的域名设置为“”。 这种方式理论上是可行的,甚至早期很多多系统登录都采用这种共享同一个域名的方式。
然而,可行并不意味着好,共享方式也有很多局限性。 首先,应用组的域名必须统一; 其次,应用组中各个系统使用的技术(至少是Web服务器)必须相同,否则键值不同,会话无法维持,共享方式无法实现跨域-语言技术平台登录。 ,例如java、php、.net系统之间; 第三,本质上是不安全的。
因此,我们需要一种新的登录方式来实现多系统应用组的登录,这就是单点登录
03单点登录
什么是单点登录?单点登录的全称是Sign On(以下简称SSO)。 意味着登录多系统应用组中的一个系统后,无需再次登录即可在所有其他系统中获得授权。 它包括单点登录和单点注销。
1. 登录
与单系统登录相比,SSO需要独立的认证中心。 只有认证中心才能接受用户的用户名、密码等安全信息。 其他系统不提供登录入口,仅接受认证中心的间接授权。 间接授权是通过token实现的。 SSO认证中心验证用户的用户名和密码是否正确,并创建授权令牌。 在接下来的跳转过程中,将授权token作为参数发送给各个子系统,子系统获取到token。 ,即您有权创建部分会话。 部分会话登录方式与单系统登录方式相同。这个过程,也就是单点登录的原理,如下图所示
下面对上图进行简单说明。
1、用户访问系统1的受保护资源,系统1发现用户未登录,跳转到SSO认证中心,并以自己的地址作为参数;
2、SSO认证中心发现用户未登录,引导用户至登录页面;
3、用户输入用户名和密码提交登录申请;
4、SSO认证中心验证用户信息,在用户和SSO认证中心之间创建会话(称为全局会话),并创建授权令牌;
5、SSO认证中心会带着token跳转到原来的请求地址(系统1);
6、系统1拿到token,去SSO认证中心验证token是否有效;
7、SSO认证中心验证token,返回有效,注册系统1;
8、系统1使用令牌与用户创建会话,称为部分会话,并返回受保护的资源;
9、用户访问系统2的受保护资源;
10、系统2发现用户未登录,跳转到SSO认证中心,并使用自己的地址作为参数;
11、SSO认证中心发现用户已登录,跳转回系统2的地址,并附上token;
12、系统2拿到token,去SSO认证中心验证token是否有效;
13、SSO认证中心验证token,返回有效,注册系统2;
14. 系统 2 使用令牌与用户创建部分会话并返回受保护的资源。
用户成功登录后,将与SSO认证中心及各子系统建立会话。 用户与SSO认证中心建立的会话称为全局会话。 用户与各子系统建立的会话称为本地会话。 本地会话建立后,用户访问子系统的受保护资源将不再经过SSO认证中心。 全局会话和本地会话有以下限制:
1、如果本地会话存在,则全局会话也必须存在;
2、全局会话存在,但本地会话可能不存在;
3、全局会话销毁,本地会话也必须销毁。
您可以通过博客园、百度、csdn、淘宝等网站的登录流程加深对单点登录的理解。 登录过程中注意跳转URL和参数。
2. 退出
当然,单点登录也需要单点注销。 如果您在某个子系统中注销,则所有子系统的会话都将被销毁。 用下图来说明
SSO认证中心始终监控全局会话的状态。 一旦全局会话被销毁,监听器将通知所有注册系统执行注销操作。
下面对上图进行简单说明。
1、用户向系统1发起注销请求;
2、系统1根据用户与系统1建立的会话ID获取token,并向SSO认证中心发起注销请求;
3、SSO认证中心验证token有效,销毁全局会话,并删除所有用该token注册的系统地址;
4、SSO认证中心向所有注册系统发起注销请求;
5、各注册系统收到SSO认证中心的注销请求,销毁部分会话;
6. sso认证中心引导用户进入登录页面。
04部署图
单点登录涉及到SSO认证中心和众多子系统。 子系统和SSO认证中心需要通信来交换令牌、验证令牌并发起注销请求。 因此,子系统必须集成SSO客户端,SSO认证中心就是SSO。 在服务器端英语作文,整个单点登录过程本质上就是SSO客户端和服务器之间的通信过程,如下图所示
SSO认证中心与SSO客户端之间的通信方式有多种。 这里我们以一个简单易用的为例。 Web、RPC、API 都可用。
05 实施
这里只是简单介绍一下基于Java的实现过程,没有提供完整的源码。 一旦你明白了原理,我相信你可以自己实现。 单点登录采用客户端/服务器架构。 我们先来看看sso-和sso-要实现的功能(下图:sso认证中心=sso-)。
单点登录-
1、拦截子系统内非登录用户的请求,并跳转至SSO认证中心;
2、接收并存储SSO认证中心发送的token;
3、与sso-通信,验证token的有效性;
4、建立本地会话;
5、拦截用户的注销请求,并将注销请求发送至SSO认证中心;
6. 接收SSO认证中心下发的下线请求,销毁部分会话。
单点登录-
1、验证用户的登录信息;
2. 创建全局会话;
3. 创建授权令牌;
4、与sso通信——发送token;
5、验证sso-token的有效性;
6、系统注册;
7. 接收 sso- 请求并注销所有会话。
接下来我们就按照原理一步步实现sso吧!
1. sso-拦截非登录请求
java中拦截请求的方式有3种,我们分别使用。 在sso-中新建一个.java类并实现接口,在()方法中添加对未登录用户的拦截。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession();
if (session.getAttribute("isLogin")) {
chain.doFilter(request, response);
return;
}
//跳转至sso认证中心
res.sendRedirect("sso-server-url-with-system-url");
}
2. sso-拦截非登录请求
拦截sso-到sso认证中心的非登录请求,并跳转到登录页面。 这个过程和sso-完全一样。
3.sso-验证用户登录信息
用户在登录页面输入用户名和密码,请求登录。SSO认证中心对用户信息进行验证。 验证成功,会话状态标记为“已登录”。
@RequestMapping("/login")
public String login(String username, String password, HttpServletRequest req) {
this.checkLoginInfo(username, password);
req.getSession().setAttribute("isLogin", true);
return "success";
}
4. sso-创建授权令牌
授权令牌是一串随机字符。 它是如何生成的并不重要,只要它不重复且不能轻易伪造即可。 这是一个例子
String token = UUID.randomUUID().toString();
5.sso-获取token并验证
登录SSO认证中心后,跳转回子系统并附加token。 子系统(sso-)获取到tokenverify什么意思,然后去SSO认证中心进行验证,在.java中的()中添加几行
// 请求附带token参数
String token = req.getParameter("token");
if (token != null) {
// 去sso认证中心校验token
boolean verifyResult = this.verify("sso-server-verify-url", token);
if (!verifyResult) {
res.sendRedirect("sso-server-url");
return;
}
chain.doFilter(request, response);
}
这里仅简单介绍一下()方法的实现。 详细使用方法请参考官方文档。
HttpPost httpPost = new HttpPost("sso-server-verify-url-with-token");
HttpResponse httpResponse = httpClient.execute(httpPost);
6. sso-接收并处理验证令牌请求
用户成功登录sso认证中心后,sso-创建授权令牌并存储该令牌。 因此,ss-验证令牌以查明令牌是否存在以及是否已过期。 token验证成功后,sso-将发送验证请求的系统注册到SSO认证中心(即存储)
令牌和注册系统地址通常存储在键值数据库(例如redis)中。 Redis可以为key设置有效期,也就是token的有效期。 Redis 在内存中运行并且速度非常快,就像 SSO 一样 - 不需要持久化数据。
令牌和注册系统地址可以使用下图描述的结构存储在redis中。 你可能会问,为什么要存储这些系统的地址?如果不存储的话,注销的时候会很麻烦。 用户向SSO认证中心提交注销请求,SSO认证中心注销全局会话。 然而,它不知道哪些系统已经使用这个全局会话建立了自己的本地会话,也不知道需要将哪些子会话发送到SSO认证中心。 系统发送注销请求,注销本地会话
7. sso- token成功创建本地会话
令牌验证成功后,sso将当前本地会话标记为“已登录”,修改.java,添加几行
if (verifyResult) {
session.setAttribute("isLogin", true);
}
sso-还需要将当前 id绑定到token上,也就是说这个的登录状态是和token相关的。 这个关系可以用java保存,保存的数据用来处理sso认证中心发送的注销请求。
8. 注销流程
用户向子系统发送带有“”参数的请求(注销请求),sso拦截器拦截该请求并向sso认证中心发起注销请求
String logout = req.getParameter("logout");
if (logout != null) {
this.ssoServer.logout(token);
}
sso认证中心也用同样的方法识别出该sso-是注销请求(带有“”参数),sso认证中心注销全局会话。
@RequestMapping("/logout")
public String logout(HttpServletRequest req) {
HttpSession session = req.getSession();
if (session != null) {
session.invalidate();//触发LogoutListener
}
return "redirect:/";
}
SSO认证中心有一个全局会话监听器。 一旦全局会话注销,所有注册的系统都会收到注销通知。
public class LogoutListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent event) {}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
//通过httpClient向所有注册系统发送注销请求
sso认证中心有一个全局会话的监听器,一旦全局会话注销,将通知所有注册系统注销
福利时间:
前线推出学习交流群一定要备注:研究/工作方向+地点+学校/公司+昵称(如目标检测+上海+上交+卡卡),根据格式备注,可更快被通过且邀请进群 扫码加我微信和进群和大佬们零距离(
(毕业后一直游离在互联网大厂)
END 开发者技术前线 ,汇集技术前线快讯和关注行业趋势,大厂干货,是开发者经历和成长的优秀指南。 历史推荐
支付宝 App 架构的原理与实战 为什么我不建议程序员做“外包”? 面试官:面阿里P7是吧?消息队列这些我必问! 12306 系统架构到底有多牛? 除了Postman之外,居然还有个Postwoman... 好文点个在看吧!