单点登出原理
我们想实现单点登出,首先会想到在当前应用下把Session销毁即可,但是仔细一想,这样的话,是不能实现把与之共享一个token的应用的Session销毁,这条路不行。
那既然所有的应用登录都需要通过一个验证服务端,那么注销的时候也通过该服务端,向所有客户端发送注销请求即可实现

代码实现
根据原理图,我们想让服务端(
sso-server
)保存用户的token在登录的时候已经实现,但现在还需要保存登出地址(客户端地址),用户的session(sessionid),因此我们可以通过一个对象来保存这两个信息,如下在服务端创建一个
ClientInfoVo
对象
@Data
public class ClientInfoVo {
private String clientUrl;
private String jsessionid;
}
有了对象,还需要将其保存到模拟的数据库中,因此在上一篇中的
MockDB
中添加一个数据结构,如下
当我们第一次登录时,需要到
/verify
判断token是否有效,因此,我们可以在校验token成功情况下将
clientUrl
和
jsessionid
存到模拟数据库中,而首先,需要将两者信息携带过去
接下来就是在校验token成功时将携带过来的信息保存到模拟数据库中,如下
至此,完成了携带登出信息并保存的过程
当我们点击注销按钮时,应该到服务端进行销毁session并通过服务端向所有客户端发起请求销毁客户端session,这就需要我们在登录的时候就把登出地址(服务端登出请求)携带,如下在客户端登录时携带上了服务端登出地址
其中获取登出地址的工具类如下,即服务端和客户端地址加上
/logOut
请求
现在当我们点击注销时,就对到服务端
www.sso;8081/logOut
请求,到这里销毁服务端session,如下到
SsoServerController
类中添加如下代码
@RequestMapping("/logOut")
public String logOut(HttpSession session){
session.invalidate();
return "login";
}
服务端session销毁了,我们还需要发起请求让所有客户端的session销毁,这里可以通过服务端的一个session监听器实现,如下
public class MySessionListener implements HttpSessionListener {
//session创建时候执行的操作
public void sessionCreated(HttpSessionEvent se) {
}
//session销毁时候执行的操作
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
String token = (String) session.getAttribute("token");
//销毁表中数据
MockDB.T_TOKEN.remove(token);
List<ClientInfoVo> clientInfoVos = MockDB.T_CLIENT_INFO.remove(token);
//遍历让验证服务端通知所有客户端销毁session
for (ClientInfoVo infoVo : clientInfoVos) {
try {
HttpUtil.sendHttpRequest(infoVo.getClientUrl(),infoVo.getJsessionid());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
并在
web.xml
中注册监听器
<!--监听器注册-->
<listener>
<listener-class>com.ranran.listener.MySessionListener</listener-class>
</listener>
既然服务端要遍历通知所有客户端销毁session,那么所有客户端也需要销毁session的方法
至此,已经完成了单点登出。