900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > SpringBoot 简单实现仿CAS单点登录系统

SpringBoot 简单实现仿CAS单点登录系统

时间:2024-04-04 12:07:37

相关推荐

SpringBoot 简单实现仿CAS单点登录系统

SpringBoot 简单实现仿CAS单点登录系统

新境界开源开源SSO项目介绍新境界开源SSO项目实现原理大致如下:新境界开源SSO项目登录流程介绍新境界开源SSO项目授权登录流程介绍新境界开源SSO项目退出流程介绍自己写单点登录Demo单点登录Demo介绍单点登录单点授权验证单点退出

新境界开源开源SSO项目介绍

最近看了一下杰哥开源的项目 /zhanghejie/jeexjj_sso 感觉写的不错.

自己根据项目的思路简单写了一个demo。在介绍demo之前我先简单介绍一下杰哥sso项目的思路。

新境界开源SSO项目实现原理大致如下:

首先要说明的是杰哥 开源 sso项目jeexjj_sso是基于cookie实现的, 通过GrantingTicketServiceTicket来完成一系列的登录操作。

首先说明一下GrantingTicketServiceTicket是什么?

GrantingTicket: 来表示用户登录的唯一标识票据。ServiceTicket:来表示每个 sso客户端应用的信息。如果一个用户登陆了基于sso的三个互相信任的系统。那么对应该用户总共有1个GrantingTicket,3个ServiceTicket

新境界开源SSO项目登录流程介绍

登陆窗口在client端的登陆流程如下:

新境界开源SSO项目授权登录流程介绍

已经登陆sso的用户首次访问某信任系统时的验证流程

新境界开源SSO项目退出流程介绍

单点退出流程

自己写单点登录Demo

单点登录Demo介绍

为了练手自己尝试写了一个Demo,但是实现思路和杰哥的 sso项目大体是一致的。涉及到sso的单点登录一般需要处理3块业务逻辑:

单点登录

就是通过sso服务端进行登录的操作。单点授权验证

当在sso服务登录成功后访问别的项目无需在次登录的操作。单点退出

当一个sso客户端退出后其他的sso客户端也需要统一退出的操作。

单点登录

大致原理如下:

我们sso客户端访问需要登录的url 或者登录的url 直接跳转到sso服务端的登录页面(如果已经登录则直接跳转到访问的url)

直接访问登录的url,sso客户端的拦截器会将其直接重定向到 sso服务端的登录页面并携带sso客户端访问地址。访问sso客户端需要登录的url(没有登录的情况下)会重定向到sso服务端单点授权验证逻辑中。如果该用户未登录则重定向到 sso服务端的登录页面并携带sso客户端访问地址。

输入用户名和密码在sso服务端进行登录,如果用户名和密码错误则重新跳转到登陆页面并提示错误信息。

如果用户名和密码正确,则生成GrantingTicket并将GrantingTicket的id 放入cookie中。然后生成ServiceTicket并将ServiceTicket放入GrantingTicket的缓存map中。

将生成GrantingTicket放入到缓存中(key:GrantingTicket的id value:GrantingTicket)然后再将ServiceTicket的id 和对应的GrantingTicket的id放入缓存中。

重定向到sso客户端 并将ServiceTicket的id(ticket)拼接到重新向的url中。

sso客户端根据ticket 再次调用sso服务端去获取用户信息。

sso服务端接受到sso客户端 传来ticket后, 根据ticket 获取缓存中的 GrantingTicket的 id,然后再根据GrantingTicket的id 获取GrantingTicket。然后再从GrantingTicket获取用户信息返回给 sso客户端。

8.sso客户端接受到用户信息后将其保存在session中。然后再将ticket和session的关系存入sso客户端 缓存的map中。

如果sso客户端根据ticket获取用户失败则返回sso服务端的登录页面并提示非法操作。

单点授权验证

大致原理如下:

当我们访问需要登录url时并且用户session信息为null的情况下 则走单点授权验证。如果该用户未登录则重定向到 sso服务端的登录页面并携带sso客户端访问地址。后面的就走sso单点登录操作。如果该用户已经在别的系统已经登录过。sso客户端拦截器拦截到该操作是单点授权验证后会重定向到sso服务端单点授权url。

4.sso服务端拦截器拦截到 单点授权url后 首先获取 cookie中GrantingTicket的id 。然后根据GrantingTicket的id获取缓存中的GrantingTicket。sso服务端 生成ServiceTicket 并放入到GrantingTicket的缓存中。sso服务端 重定向到sso客户端并携带ServiceTicket 的id (ticket)。sso客户端拦截器拦截到重定向url并获取到ticket 。然后根据ticket从sso服务端获取用户的信息。sso服务端接受到sso客户端ticket 根据ticket 获取缓存中的 GrantingTicket的id,然后再根据GrantingTicket的id 获取GrantingTicket。然后再从GrantingTicket获取用户信息,返回给客户端。sso客户端接受到用户的信息后 将信息保存在session中然后将 ticket 和sesion的关系对应保存在缓存中。

单点退出

sso客户端点击退出按钮,会向sso服务端发起退出登录操作。sso服务端拦截器拦截到退出的操作后会将进入退出的操作逻辑sso服务端 第一步先获取sso服务端在存储的GrantingTicket的cookie信息根据在cookie中存储的GrantingTicket的id 从缓存中获取到GrantingTicket。然后将sso服务端在存储的GrantingTicket的cookie信息清除。获取GrantingTicket 缓存的ServiceTicket缓存map信息。遍历ServiceTicket的Map信息。通过ServiceTicket的host信息依次调用客户端的退出url并携带ticket。sso客户端拦截器拦截到退出的操作并获取到ticket.根据之前在客户端缓存的map中根据ticket获取到sesion的信息并调用sess.invalidate();sso服务端重定向到sso服务端的登录页面。

sso客户端拦截器代码:

public class SSOClientFilter implements Filter{private final String SUCESSURL = "/index/index";private final String SSOSERVERLOGINURL = "http://localhost:8080/ssoServer/login/login";private final String SSOSERVERAUTHENTICATIONURL = "http://localhost:8080/ssoServer/sso/authentication";public static ConcurrentHashMap<String,HttpSession> TICKET_SESSION_CACHE = new ConcurrentHashMap<String,HttpSession>();@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest )servletRequest;HttpServletResponse response = (HttpServletResponse )servletResponse;HttpSession session = request.getSession();String path = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();String uri = request.getRequestURI();//被动单点退出String requestLogoutTicket = request.getParameter("requestLogout");if(!StringUtils.isEmpty(requestLogoutTicket)){ssoLogout(requestLogoutTicket);response.sendRedirect(SSOSERVERLOGINURL+"?service="+path);return;}//根据ticket获取用户信息String ticket = request.getParameter("ticket");if(!StringUtils.isEmpty(ticket)){User user = getUserByTicket(ticket);if(user == null){response.sendRedirect(SSOSERVERLOGINURL+"ERRORMSG="+"非法访问");return;}else{session.setAttribute("LOGIN_INFO", user);response.sendRedirect(path+SUCESSURL);TICKET_SESSION_CACHE.put(ticket, session);return;}}User user = (User)session.getAttribute("LOGIN_INFO");if(uri.endsWith("/login")){response.sendRedirect(SSOSERVERLOGINURL+"?service="+path);return;}if(null != user || uri.endsWith("css") || uri.endsWith("js")){filterChain.doFilter(servletRequest, servletResponse);return;}else{response.sendRedirect(SSOSERVERAUTHENTICATIONURL+"?service="+path);return;}}private void ssoLogout(String ticket) {if(TICKET_SESSION_CACHE.containsKey(ticket)){HttpSession sess = TICKET_SESSION_CACHE.get(ticket);if(null != sess){sess.invalidate();}TICKET_SESSION_CACHE.remove(ticket);}}private User getUserByTicket(String ticket) {User user = null;CloseableHttpResponse closeableHttpResponse = null;try {CloseableHttpClient httpClient = HttpClients.createDefault();URI uriHttp = uriHttp = new URI("http://localhost:8080/ssoServer/sso/validateTicket");URIBuilder uriBuilder = new URIBuilder(uriHttp);uriBuilder.setParameter("ticket", ticket);URI uriParma = uriBuilder.build();HttpGet httpGet = new HttpGet(uriParma);//执行请求访问closeableHttpResponse = httpClient.execute(httpGet);//获取返回HTTP状态码int satausCode = closeableHttpResponse.getStatusLine().getStatusCode();if(satausCode == 200 ){HttpEntity entity = closeableHttpResponse.getEntity();String content = EntityUtils.toString(entity,"UTF-8");user = JSONUtil.toBean(content, User.class);EntityUtils.consume(entity);}} catch (ClientProtocolException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (URISyntaxException e) {e.printStackTrace();}finally{try {closeableHttpResponse.close();} catch (IOException e) {e.printStackTrace();}}return user;}}

sso服务端拦截器代码:

public class SSOServerFilter implements Filter{private final String LOGINURL = "http://localhost:8080/ssoServer/login/login";private final String cookieName = "zhuoqianmingyue";@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest )servletRequest;HttpServletResponse response = (HttpServletResponse )servletResponse;String uri = request.getRequestURI();if(uri.endsWith("sso/validateTicket")){String ticket = request.getParameter("ticket");String service = request.getParameter("service");String gtId = TicketCache.getGTByST(ticket);GrantingTicket grantingTicket = TicketCache.getGrantingTicket(gtId);User user = grantingTicket.getUser();String jsonStr = JSONUtil.toJsonStr(user);//让ticket过期ServiceTicket serviceTicket = grantingTicket.getServiceTicketMap().get(ticket);boolean expired = serviceTicket.isExpired();if(expired){serviceTicket.setExpired(false);response.getOutputStream().write(jsonStr.getBytes("UTF-8"));return;}else{response.sendRedirect(LOGINURL+"?srevice="+service+"&ERRORMSG=ticket已经过期!");return;}}else if(uri.endsWith("sso/signin")){String loginName = request.getParameter("loginName");String password = request.getParameter("password");String service = request.getParameter("service");if("lijunkui".equals(loginName)){UUID randomUUID = UUID.randomUUID();String ticketId = randomUUID.toString();GrantingTicket gt = new GrantingTicket();gt.setId(ticketId);User user = new User();user.setUserId(1l);user.setLoginName("lijunkui");user.setPassword("123456");gt.setUser(user);Cookie cookie = new Cookie("zhuoqianmingyue", ticketId);cookie.setMaxAge(-1);cookie.setPath("/");response.addCookie(cookie);TicketCache.putGrantingTicket(ticketId, gt);ServiceTicket st = new ServiceTicket();String stId = UUID.randomUUID().toString();st.setId(stId);st.setHost(service);ConcurrentHashMap<String, ServiceTicket> serviceTicketMap = gt.getServiceTicketMap();serviceTicketMap.put(stId, st);TicketCache.putSTAndGT(stId, ticketId);response.sendRedirect(service+"?ticket="+st.getId());return;}else{response.sendRedirect(LOGINURL+"?srevice="+service+"&ERRORMSG=登录名或者密码错误");return;}}else if(uri.endsWith("sso/authentication")){String gtId = getSSOCookie(request,response);String service = request.getParameter("service");GrantingTicket gt = null;if(gtId != null){gt = TicketCache.getGrantingTicket(gtId);}if(StringUtils.isEmpty(service) || null == gtId || null == gt){response.sendRedirect(LOGINURL+"?srevice="+service);return;}ServiceTicket st = new ServiceTicket();String stId = UUID.randomUUID().toString();st.setId(stId);st.setHost(service);ConcurrentHashMap<String, ServiceTicket> serviceTicketMap = gt.getServiceTicketMap();serviceTicketMap.put(stId, st);TicketCache.putSTAndGT(stId, gt.getId());response.sendRedirect(service+"?ticket="+st.getId());return;}else if(uri.endsWith("sso/signout")){String gtCookie = getSSOCookie(request, response);String service = request.getParameter("service");ssoLogout(request,response,gtCookie);response.sendRedirect(LOGINURL+"?service="+service);return;}filterChain.doFilter(servletRequest, servletResponse);}

测试步骤:

点单登录Demo并没有对接mysql数据库 在进行部署测试的时候请使用 登录名:lijunkui 密码:123456 进行登录访问 http://localhost:8090/ssoClient/index/index 或者

http://localhost:8090/ssoClient/login 进行登录登录成功后会跳转到 http://localhost:8090/ssoClient/index/index。然后再访问 http://localhost:8070/ssoClient2/index/index 进行测试是时候可以直接登录。

示例程序代码地址:/junkuiLee/ssoexamples

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。