转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/11377620.html
一:单点登录业务接入场景
对于大型企业,内部有各种各样的办公OA、业务系统,员工使用起来要记住不同系统的账号密码,非常不方便,因此就催生出一种统一管理账号的认证系统,即单点登录系统。
其作用是,某位员工,在其中一个系统进行登录验证后,再打开其他系统时,就不需要再次登录,直接进入系统,十分方便快捷。
那怎么才能做到呢?
二:接入思路原理
1、单点登录系统
首先,我们要有一个单点登录系统,对账号进行统一管理,我们可以将它作为一个微服务进行部署。
在这个系统中,记录了员工的账号信息,如:工号、姓名、电话等等。
2、业务系统
在业务系统中,也要有对应的用户账户,因为业务系统的用户只是单点登录系统用户的一个子集或者交集而已。
并且,业务系统中有自己的一套角色管理以及授权机制。
3、单点登录原理
1)在登录业务系统时,拦截未登录的请求,重定向到单点登录扫码认证接口;
2)在单点登录页面扫码或密码登录后,单点登录系统重定向回业务系统,并且在session中携带认证账号信息;
3)在业务系统通过过滤器,从session中尝试提取账号信息,如果提取到了,则根据账号信息[通常是用户的工号或者loginname];
然后根据账号信息,从业务系统本身的数据库中查询出对应的用户账户名和登录密码,如果有,则说明此登录用户是本系统用户,直接调用本系统所用的登录校验机制在代码中模拟一遍登录过程,并把登录后状态信息保存到session中即可;如果找不到对应用户,则说明此用户在业务系统没有账号,禁止登入,重定向回系统登录页面或者一个消息提示页面告诉他没有系统账户。
三:接入实现
以下基于Java进行接入说明。
1、添加依赖
Java的单点登录功能依赖于 cas_client_core 这个jar包,我们可以在pom文件中添加依赖:
<dependency> <groupId>org.jasig.cas.clientgroupId> <artifactId>cas-client-coreartifactId> <version>3.2.0version> dependency>
2、配置拦截规则
1)对于非SpringBoot构建的项目,在web.xml中配置以下拦截规则:
<listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListenerlistener-class> listener> <filter> <filter-name>CAS Single Sign Out Filterfilter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilterfilter-class> filter> <filter-mapping> <filter-name>CAS Single Sign Out Filterfilter-name> <url-pattern>/*url-pattern> filter-mapping> <filter> <filter-name>CASFilterfilter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilterfilter-class> <init-param> <param-name>casServerLoginUrlparam-name> <param-value>单点登录系统认证接口param-value> init-param> <init-param> <param-name>serverNameparam-name> <param-value>本业务系统首页地址param-value> init-param> filter> <filter-mapping> <filter-name>CASFilterfilter-name> <url-pattern>/*url-pattern> filter-mapping> <filter> <filter-name>CAS Validation Filterfilter-name> <filter-class> org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilterfilter-class> <init-param> <param-name>casServerUrlPrefixparam-name> <param-value>单点登录系统认证接口param-value> init-param> <init-param> <param-name>serverNameparam-name> <param-value>本业务系统首页地址param-value> init-param> filter> <filter-mapping> <filter-name>CAS Validation Filterfilter-name> <url-pattern>/*url-pattern> filter-mapping> <filter> <filter-name>CAS HttpServletRequest Wrapper Filterfilter-name> <filter-class> org.jasig.cas.client.util.HttpServletRequestWrapperFilterfilter-class> filter> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filterfilter-name> <url-pattern>/*url-pattern> filter-mapping> <filter> <filter-name>CAS Assertion Thread Local Filterfilter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilterfilter-class> filter> <filter-mapping> <filter-name>CAS Assertion Thread Local Filterfilter-name> <url-pattern>/*url-pattern> filter-mapping>
2)对于SpringBoot构建的项目,则需创建一个配置类,在其中通过方法来返回配置bean。
首先,创建一个类,继承 WebMvcConfigurer,并重写两个方法:
然后,定义过滤器配置方法,注意:一个过滤器定义一个方法,该方法返回一个配置bean:
方法中要设置的内容,参考web.xml下配置的内容即可。
3、编写业务系统的登录过滤器
在业务系统处定义一个过滤器,对所有路径进行拦截校验:如果session中已有登录状态,则放行;否则,尝试从session中提取cas认证信息,并进行模拟登录逻辑;如果也没有,则重定向到业务系统登录页面使用户进行登录操作。
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest re = (HttpServletRequest) request; //获取request HttpServletResponse resp = (HttpServletResponse) response; //获取respinse AttributePrincipal attributePrincipal = (AttributePrincipal) re.getUserPrincipal(); //返回一个java.security.Principal 对象,该对象包含当前授权用户的名称 String cas_account = ""; String username = ""; String userpass = ""; //提前session中的登录信息 Object object = re.getSession().getAttribute("_const_cas_assertion_"); if (object != null) { Assertion assertion = (Assertion) object; fnumber = assertion.getPrincipal().getName(); } else if (attributePrincipal != null) { cas_account = (String) attributePrincipal.getName(); } Users userLogin = (Users) re.getSession().getAttribute("CURRENT_USER"); if (userLogin == null) { if(cas_account ==null||"".equals(cas_account )){ chain.doFilter(request, response);//本系统通过拦截器进行了登录状态校验,故此处放行,让后面的拦截器进行未登录重定向,而不在此处进行。当然,也可以在此处直接重定向回首页 } }else{ chain.doFilter(request, response);//若检测到登录信息,则直接放行 } //检测到cas信息,但是又未登录,则在此处模拟登录过程 try { if (re.getSession().getAttribute("CURRENT_USER") == null) { //代码模拟登录过程:根据cas传过来的用户信息,查找本系统用户,拿到账号密码进行登录认证
模拟过程略......各系统不一样,按需实现。
re.getSession().setAttribute("CURRENT_USER", 当前登录用户); resp.sendRedirect("/index?userId="+当前登录用户id);//登录成功,重定向到业务系统首页 }else { if(users.size() == 0){ logger.error("========users match none!========");//根据cas信息,在本系统没找到匹配用户,则返回系统登录页面或者重定向到一个错误提示页 }else { logger.error("========users match more than one!========");//根据cas信息,在本系匹配到多于一个用户,则返回系统登录页面或者重定向到一个错误提示页 } } } } else { if(mobiles.size() == 0){ logger.error("========mobile match none!========"); }else { logger.error("========mobile match more than one!========"); } } } } catch (Exception e) { e.printStackTrace(); } chain.doFilter(request, response); }
最后,别忘了将自定义的filter配置到web.xml或者在配置类中书写一个注册方法。