单点登录
1.单点登录这块怎么实现的
1.1 概述
单点登录的英文名叫做:Single Sign On(简称SSO),只需要登录一次,就可以访问所有信任的应用系统
在以前的时候,一般我们就单系统,所有的功能都在同一个系统上。
单体系统的session共享
-
登录:将用户信息保存在Session对象或Request的Header(请求头)中,(这部分通常是前端实现的,通常是前端应用的状态管理中,如JavaScript变量、Web Storage(localStorage/sessionStorage))并在每次请求时手动附加到请求中是一种常见做法。)服务器则将用户信息保存在redis中,并设置过期时间。
-
通过拦截器校验是否登录
-
- 如果在Session对象中能查到,说明已经登录
- 如果在Session对象中查不到,说明没登录(或者已经退出了登录)
-
注销(退出登录):从Session中删除用户的信息
对Session不了解的同学可以观看这篇博客:Session详解,学习 Session对象一篇文章就够了
后来,我们为了合理利用资源和降低耦合性,于是把单系统拆分成多个子系统。
多系统即可能有多个Tomcat,而Session是依赖当前系统的Tomcat,所以系统A的Session和系统B的Session是不共享的。
解决系统之间Session不共享问题有一下几种方案:
- Tomcat集群Session全局复制(最多支持5台tomcat,不推荐使用)
- JWT(常见)
- Oauth2
- CAS
- 自己实现(redis+token)
1.2 JWT解决单点登录
现在有一个微服务的简单架构,如图:
通过网关统一拦截请求,校验是否登录,没登录返回401,登录了则路由到对应的微服务。
使用jwt解决单点登录的流程如下:
不懂JWT使用的同学可以看我另外两篇博客JWT令牌快速入门、JWT实战教程学习JWT令牌技术。
由于微服务获取用户信息是通过拦截器在请求头中读取,因此要想实现微服务之间的用户信息传递,就必须在微服务发起调用时把用户信息存入请求头。
微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。我们如何才能让每一个由OpenFeign发起的请求自动携带登录用户信息呢?
这里要借助Feign中提供的一个拦截器接口:feign.RequestInterceptor
public interface RequestInterceptor {
/**
* Called for every request.
* Add data using methods on the supplied {@link RequestTemplate}.
*/
void apply(RequestTemplate template);
}
我们只需要实现这个接口,然后实现apply方法,利用RequestTemplate类来添加请求头,将用户信息保存到请求头中。这样以来,每次OpenFeign发起请求的时候都会调用该方法,传递用户信息。
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
// 获取登录用户
Long userId = UserContext.getUser();
if(userId == null) {
// 如果为空则直接跳过
return;
}
// 如果不为空则放入请求头中,传递给下游微服务
template.header("user-info", userId.toString());
}
};
}
好了,现在微服务之间通过OpenFeign调用时也会传递登录用户信息了。
1.3 回答要点
1,先解释什么是单点登录
单点登录的英文名叫做:Single Sign On(简称SSO)
2,介绍自己项目中涉及到的单点登录(即使没涉及过,也可以说实现的思路)
3,介绍单点登录的解决方案,以JWT为例
I. 用户访问其他系统,会在网关判断token是否有效
II. 如果token无效则会返回401(认证失败)前端跳转到登录页面
III. 用户发送登录请求,返回浏览器一个token,浏览器把token保存到cookie
IV. 再去访问其他服务的时候,都需要携带token,由网关统一验证后路由到目标服务