主要知识点:理解session机制、理解重定向、理解filter机制、会使用httpclient。
单点登录,各个子系统和认证中心的交流是通过token,浏览器与子系统之间和浏览器与认证中心之间,是通过session直接交流。
浏览器只能看得到子系统,不能直接接触认证中心。当浏览器需要子系统为其创建会话时,子系统需要将浏览器重定向到认证中心,这时浏览器直接与认证中心交流。
浏览器在认证中心登录成功后,认证中心创建全局会话,并且帮助子系统创建局部会话。
由认证中心重定向到子系统,重定向时把认证中心在创建全局会话时生成的与子系统之间交流的标志token带上,子系统由token确认是哪个用户在认证中登录了。
子系统确认时,用httpclient与认证中心进行通信,去验证收到的token。验证成功,并且收到认证中心发过来的登录的用户id,则创建局部会话,并且在局部会话过期之前不会再与认证中心交流。
当切换系统时,由切换后的系统重定向到认证中心。如果认证中心的全局会话没有过期,则认证中心直接读取保存过的数据,并且带着token重定向到切换后的系统。以后同上。
单点登录主要就是利用了浏览器的session机制,利用了重定向。
下面是客户端的filter过滤器,除了配置文件,主要就是过滤器。redis的使用不是必须的,只要有能存储全局变量的数据结构就可以。
// 如果请求参数中有logout字样,则退出登陆
// 子系统不再在filter中向sso发送登陆请求了,可以在退出登陆界面向sso发送退出请求
String logout = request.getParameter("logout");
if (null != logout) {
String code = request.getParameter("code");
String sessionId = RedisUtil.get(.+ "_" + code);
RedisUtil.remove(.+ "_" + sessionId);
RedisUtil.remove(.+ "_" + code);
RedisUtil.remove(.+ "_" + sessionId);
chain.doFilter(request, response);
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendRedirect(.
+ "?backurl="
+ URLEncoder.encode(FkGouConfig.FKGOUPAY, "utf-8"));
return;
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpSession session = httpRequest.getSession();
String sessionId = session.getId();
// 不使用isLogin是因为isLogin在退出时销毁不方便,销毁时,sso给个人系统发送消息,拿不到session
// 根据sessioniid再redis中存在与否,判断是否登陆了。如果登陆了,则继续执行filter
String cacheClientCode = RedisUtil.get(.+ "_" + session.getId());
if (null != cacheClientCode && !"".equals(cacheClientCode)) {
// 更新code有效期
RedisUtil.set(.+ "_" + sessionId, cacheClientCode, 40);
Jedis jedis = RedisUtil.getJedis();
jedis.expire(.+ "_" + cacheClientCode, 40);
jedis.close();
// 继续进行操作
chain.doFilter(request, response);
}
// 应该在request中取code,因为sso可能会发送code过来
String code = httpRequest.getParameter("code");
// 若存在,则去sso验证是否存在code
if (null != code && !"".equals(code)) {
// HttpPost去校验code
HttpClient httpclient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(.+ "/sso/code.do");
List<NameValuePair> nameValuePairs = new ArrayList<>();
nameValuePairs.add(new BasicNameValuePair("code", code));
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse httpResponse = httpclient.execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity httpEntity = httpResponse.getEntity();
JSONObject result = JSONObject.parseObject(EntityUtils.toString(httpEntity));
System.out.print("JSONObject result:" + result.toString());
if (1 == result.getIntValue("code") && result.getString("data").equals(code)) {
// code校验正确,创建局部会话
RedisUtil.set(.+ "_" + sessionId, code, 40);
// 保存code对应的局部会话sessionId,方便退出操作
RedisUtil.set(.+ "_" + code, sessionId, 40);
// client无密认证
String userId = result.getString("message");
RedisUtil.set(.+ "_" + sessionId, userId, 40);
session.setAttribute("username", userId);
chain.doFilter(request, response);
}
}
}
HttpServletResponse httpResponse = (HttpServletResponse) response;
String backurlString = URLEncoder.encode(., "utf-8");
httpResponse.sendRedirect(.+"?backurl=" + backurlString);
return;
服务端,主要就是登陆接口,验证token接口,注销接口。