對於大型企業,內部有各類各樣的辦公OA、業務系統,員工使用起來要記住不一樣系統的帳號密碼,很是不方便,所以就催生出一種統一管理帳號的認證系統,即單點登陸系統。html
其做用是,某位員工,在其中一個系統進行登陸驗證後,再打開其餘系統時,就不須要再次登陸,直接進入系統,十分方便快捷。java
那怎麼才能作到呢?web
一、單點登陸系統數據庫
首先,咱們要有一個單點登陸系統,對帳號進行統一管理,咱們能夠將它做爲一個微服務進行部署。session
在這個系統中,記錄了員工的帳號信息,如:工號、姓名、電話等等。app
二、業務系統ide
在業務系統中,也要有對應的用戶帳戶,由於業務系統的用戶只是單點登陸系統用戶的一個子集或者交集而已。微服務
而且,業務系統中有本身的一套角色管理以及受權機制。url
三、單點登陸原理spa
1)在登陸業務系統時,攔截未登陸的請求,重定向到單點登陸掃碼認證接口;
2)在單點登陸頁面掃碼或密碼登陸後,單點登陸系統重定向回業務系統,而且在session中攜帶認證帳號信息;
3)在業務系統經過過濾器,從session中嘗試提取帳號信息,若是提取到了,則根據帳號信息[一般是用戶的工號或者loginname];
而後根據帳號信息,從業務系統自己的數據庫中查詢出對應的用戶帳戶名和登陸密碼,若是有,則說明此登陸用戶是本系統用戶,直接調用本系統所用的登陸校驗機制在代碼中模擬一遍登陸過程,並把登陸後狀態信息保存到session中便可;若是找不到對應用戶,則說明此用戶在業務系統沒有帳號,禁止登入,重定向回系統登陸頁面或者一個消息提示頁面告訴他沒有系統帳戶。
如下基於Java進行接入說明。
一、添加依賴
Java的單點登陸功能依賴於 cas_client_core 這個jar包,咱們能夠在pom文件中添加依賴:
<!-- https://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core --> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.2.0</version> </dependency>
二、配置攔截規則
1)對於非SpringBoot構建的項目,在web.xml中配置如下攔截規則:
<!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置 --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!-- 該過濾器用於實現單點登出功能,可選配置。 --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 該過濾器負責用戶的認證工做,必須配置 --> <filter> <filter-name>CASFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>單點登陸系統認證接口</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>本業務系統首頁地址</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 該過濾器負責對Ticket的校驗工做,必須啓用它 --> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class> org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>單點登陸系統認證接口</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>本業務系統首頁地址</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 該過濾器負責實現HttpServletRequest請求的包裹, 好比容許開發者經過HttpServletRequest的getRemoteUser()方法得到SSO登陸用戶的登陸名,可選配置。 --> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class> org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 該過濾器使得開發者能夠經過org.jasig.cas.client.util.AssertionHolder來獲取用戶的登陸名。 好比AssertionHolder.getAssertion().getPrincipal().getName()。 --> <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2)對於SpringBoot構建的項目,則需建立一個配置類,在其中經過方法來返回配置bean。
首先,建立一個類,繼承 WebMvcConfigurer,並重寫兩個方法:
而後,定義過濾器配置方法,注意:一個過濾器定義一個方法,該方法返回一個配置bean:
方法中要設置的內容,參考web.xml下配置的內容便可。
三、編寫業務系統的登陸過濾器
在業務系統處定義一個過濾器,對全部路徑進行攔截校驗:若是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或者在配置類中書寫一個註冊方法。