单点登录框架有哪些(单点登录失败解决措施)

单点登录在大型网站里使用得非常频繁,例如,阿里旗下有淘宝、天猫、支付宝,阿里巴巴,阿里妈妈,阿里妹妹等网站,还有背后的成百上千的子系统,用户一次操作或交易可能涉及到几十个子系统的协作,如果每个子系统都需要用户认证,不仅用户会疯掉,各子系统也会为这种重复认证授权的逻辑搞疯掉。例如阿里系的淘宝、天猫、支付宝、钉钉 ;百度的百度知道、文库、贴吧等等都是采用了这样的一套体系。那么今天我们就详细的走入单点登录讲解吧!

什么是单点登录

单点登录要解决的就是,用户只需要登录一次就可以访问所有相互信任的应用系统。

单点登录原理、Http通信实现

1.2、SSO概念

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO 并不能算是一种架构,只能说是一个解决方案。SSO核心意义就一句话:一处登录,处处登录;一处注销,处处注销。就是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,即用户只需要记住一组用户名和密码就可以登录所有有权限的系统。

较大的企业内部,一般都有很多的业务支持系统为其提供相应的管理和IT服务。这些不同的系统往往是在不同的时期建设起来的,运行在不同的平台上;企业应用集成可以在不同层面上进行:例如在数据存储层面 上的“数据大集中”,在传输层面上的“通用数据交换平台”,在应用层面上的“业务流程整合”,和用户界面上的“通用企业门户”等等。事实上,还用一个层面 上的集成变得越来越重要,那就是“身份认证”的整合,也就是“单点登录”。

另外,使用“单点登录”还是SOA时代的需求之一。在面向服务的架构中,服务和服务之间,程序和程序之间的通讯大量存在,服务之间的安全认证是SOA应用的难点之一,应此建立“单点登录”的系统体系能够大大简化SOA的安全问题,提高服务之间的合作效率。

单点登录的机制其实是比较简单的,用一个现实中的例子做比较。颐和园是北京著名的旅游景点,也是我常去的地方。在颐和园内部有许多独立的景点,例如“苏州街”、“佛香阁”和“德和园”,都可以在各个景点门口单独买票。很多游客需要游玩所有德景点,这种买票方式很不方便,需要在每个景点门口排队买票,钱包拿 进拿出的,容易丢失,很不安全。于是绝大多数游客选择在大门口买一张通票(也叫套票),就可以玩遍所有的景点而不需要重新再买票。他们只需要在每个景点门 口出示一下刚才买的套票就能够被允许进入每个独立的景点。

单点登录的机制也一样,如下图所示,当用户第一次访问应用系统1的时候,因为还没有登录,会被引导到认证系统中进行登录(1);根据用户提供的登录信息, 认证系统进行身份效验,如果通过效验,应该返回给用户一个认证的凭据--ticket(2);用户再访问别的应用的时候(3,5)就会将这个ticket 带上,作为自己认证的凭据,应用系统接受到请求之后会把ticket送到认证系统进行效验,检查ticket的合法性(4,6)。如果通过效验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。

单点登录框架有哪些(单点登录失败解决措施)

1.3、简谈SSO的发展

1.3.1、早期的单机部署

早期我们开发web应用都是所有的包放在一起打成一个war包放入tomcat容器来运行的,所有的功能,所有的业务,后台管理,门户界面,都是由这一个war来支持的,这样的单应用,也称之为单体应用,因为十分不好扩展和拆分。在单体应用下,用户的登录以及权限就显得十分简单:过滤器,用户登录成功后,把相关信息放入会话中,HTTP维护这个会话,再每次用户请求服务器的时候来验证这个会话即可验证登录的这个会话就是session,维护了用户状态,也就是所谓的HTTP有状态协议,我们经常可以在浏览器中看到JSESSIONID,这个就是用来维持这个关系的key。

1.3.2、分布式集群部署

1.4、Cookie单点登录

单点登录的实现方案,一般就包含以下三种:

Cookies

Session同步

分布式Session方式

目前的大型网站都是采用分布式Session的方式。我先从cookie的实现谈起,你就能很清楚的知道为什么需要分布式session方式实现单点登录。

基于Cookie的单点登录

最简单的单点登录实现方式,是使用cookie作为媒介,存放用户凭证。用户登录父应用之后,应用返回一个加密的cookie,当用户访问子应用的时候,携带上这个cookie,授权应用解密cookie并进行校验,校验通过则登录当前用户。不难发现以上方式把信任存储在客户端的Cookie中,这种方式很容易令人质疑:

Cookie不安全

不能跨域实现免登

对于第一个问题,通过加密Cookie可以保证安全性,当然这是在源代码不泄露的前提下。如果Cookie的加密算法泄露,攻击者通过伪造Cookie则可以伪造特定用户身份,这是很危险的。

对于第二个问题,不能跨域实现免登更是硬伤。所以,才有了以下的分布式session方案。

1.5、分布式session单点登录

例如,阿里有很多系统分割为多个子系统,独立部署后,不可避免的会遇到会话管理的问题,类似这样的电商网站一般采用分布式Session实现。再进一步可以根据分布式Session redis,建立完善的单点登录或账户管理系统。

例如,阿里有很多系统分割为多个子系统,独立部署后,不可避免的会遇到会话管理的问题,类似这样的电商网站一般采用分布式Session实现。再进一步可以根据分布式Session redis,建立完善的单点登录或账户管理系统。

1.6、常见方案

实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性,因此要点也就以下两个:

存储信任 ~服务器生产~

验证信任 ~ 拿到服务器再次验证~

单点登录的常见落地实现技术有哪些?

身份认证技术:

1. cas(单点登录)

3. jwt (客户端token:原生)

安全控制框架:

1. spring-security

2. shiro:

1.6.1、cas(单点登录)

解决问题:多个系统只需登录一次,无需重复登录

原理:授权服务器,被授权客户端 CS架构

1. 授权服务器(一个)保存了全局的一份session,客户端(多个)各自保存自己的session

2. 客户端登录时判断自己的session是否已登录,若未登录,则(告诉浏览器)重定向到授权服务器

3. 授权服务器判断全局的session是否已登录,若未登录则定向到登录页面,提示用户登录,登录成

功后,授权服务器重定向到客户端(参数带上ticket【一个凭证号】)

4. 客户端收到ticket后,请求服务器获取用户信息

5. 服务器同意客户端授权后,服务端保存用户信息至全局session,客户端将用户保存至本地session

6. 默认不支持http请求, 仅支持https 。生成证书 keytools

缺点:cas单点登录技术适用于传统应用的场景比较多, 官方示例也是以javaWeb为准, 对微服务化应用,前后端分离应用,支持性较差。

1.6.2、oauth2(第三方登录授权)

解决问题:第三方系统访问主系统资源,用户无需将在主系统的账号告知第三方,只需通过主系统的授权,第三方就可使用主系统的资源

OAuth2是用来允许用户授权第三方应用访问他在另一个服务器上的资源的一种协议,它不是用来做单点登录的,但我们可以利用它来实现单点登录。

原理:主系统,授权系统(给主系统授权用的,也可以跟主系统是同一个系统),第三方系统。

1. 第三方系统需要使用主系统的资源,第三方重定向到授权系统

2. 根据不同的授权方式,授权系统提示用户授权

3. 用户授权后,授权系统返回一个授权凭证(accessToken)给第三方系统【accessToken是有有效期的】

4. 第三方使用accessToken访问主系统资源【accessToken失效后,第三方需重新请求授权系统,以获取新的accessToken】

Resource Server: 被授权访问的资源

Authotization Server:OAUTH2认证授权中心

Resource owner : 资源拥有者

Client:使用API的客户端(如Android 、IOS、web app)

1.6.3、jwt (客户端token)

难度较大,需要你了解很多协议~Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。基于JWT认证协议,自己开发SSO服务和权限控制。

1.6.4、SpringSecurity

springSecurity 是spring家族的安全控制框架, 功能非常完善。

Spring Security是能够为J2EE项目提供综合性的安全访问控制解决方案的安全框架。

spring-boot-starter-springsecurity

它依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处

理。

1.6.5、Shiro

Apache Shiro 是一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理

和加密。以下是 Apache Shiro 可以做的事情:

1. 验证用户来核实他们的身份

2. 对用户执行访问控制

3. 判断用户是否被分配了一个确定的安全角色

4. 判断用户是否被允许做某事

5. 在任何环境下使用 Session API,即使没有 Web 或 EJB 容器。

6. 在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。

7. 聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。

8. 启用单点登录(SSO)功能。内置了jasig-cas

9. 为没有关联到登录的用户启用”Remember Me”服务。

技术人员态度:通吃才对!

1.7、市面主流的技术搭配

代码实现

前端: Vue Aixos

sso登录中心、淘宝、天猫实现单点登录

2.1、HttpURLConnection

在JDK的java.net包中已经提供了访问Http协议的基本功能的类:HttpURLConnectionHttpURLConnection 是 java 的标准类,继承自 URLConnection,可用于向指定网站发送GET请求、Post请求。

1. 京东万象 https://wx.jdcloud.com/

2. 没有注册的小伙伴,需要注册一个账号用来登录,1分钟实现一个个人认证

3. 在API集市中,找到免费的一些API,比如:中国和世界天气预报

4. 测试数据

了解完API接口后,我们可以去编写个Demo体验下:

1. 新建一个Maven父工程 SSO ,删掉无用项

2. 新建一个Module;sso_demo01

3. 编写代码测试

4. 测试成功,返回到响应的 JSON 字符串

5. 我们将这段代码,抽取为一个工具类;

6. 我们之后直接使用这个工具类就好!十分的方便~

package com.icodingedu.sso;import com.icodingedu.utils.HttpUtil;import java.util.HashMap;public class HttpTest {public static void main(String[] args) throws Exception {//Url:https://way.jd.com/he/freeweather?city=beijing&appkey=06642046425c68a351817b5b020b591fHashMap<String, String> map = new HashMap<String, String>();map.put(“city”,”beijing”);map.put(“appkey”,”06642046425c68a351817b5b020b591f”);System.out.println(HttpUtil.sendHttpRequest(“https://way.jd.com/he/freeweather”, map));}}

2.2、单点登录手写实现

配置一些 host文件,模拟跨域!管理员模式!

127.0.0.1 www.sso.com127.0.0.1 www.tb.com127.0.0.1 www.tm.com

【演示】

相比于单系统登录,SSO 需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。

间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题。创建授权令牌,在接下来的挑战过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同,这个过程,也就是单点登录的原理。

2.2.1、基础工程建立

1. 新建一个maven项目,建立两个字module sso-server , sso-client-tb

2. 导入需要的依赖,SpringMVC,JSP,Servlet 等等

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.2.1-b03</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version></dependency></dependencies>

3. 将所有项目都改变成Web项目,建立SpringMVC结构

web.xml

<?xml version=”1.0″ encoding=”UTF-8″?><web-app xmlns=”http://xmlns.jcp.org/xml/ns/javaee”xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”xsi:schemaLocation=”http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd”version=”4.0″><!–DispatcherServlet–><servlet><servlet-name>DispatcherServlet</servlet-name><servletclass>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!–encodingFilter–><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>

applicationContext.xml

<?xml version=”1.0″ encoding=”UTF-8″?><beans xmlns=”http://www.springframework.org/schema/beans”xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”xmlns:context=”http://www.springframework.org/schema/context”xmlns:mvc=”http://www.springframework.org/schema/mvc”xsi:schemaLocation=”http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd”><!– 1.开启SpringMVC注解驱动 –><mvc:annotation-driven /><!– 2.静态资源默认servlet配置–><mvc:default-servlet-handler/><!– 3.配置jsp 显示ViewResolver视图解析器 –><beanclass=”org.springframework.web.servlet.view.InternalResourceViewResolver”><property name=”viewClass”value=”org.springframework.web.servlet.view.JstlView” /><property name=”prefix” value=”/WEB-INF/views/” /><property name=”suffix” value=”.jsp” /></bean><!– 4.扫描web相关的bean –><context:component-scan base-package=”com.icoding” /></beans>

2.2.2、服务端登录业务

1. 导入准备好的静态资源素材

2. 服务端登录页面编写

<%@ page contentType=”text/html;charset=UTF-8″ language=”java” %><!DOCTYPE html><html lang=”en”><head><meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″><meta name=”viewport” content=”width=device-width, initial-scale=1,shrink-to-fit=no”><title>Sso-Server:登录</title><linkhref=”${pageContext.request.contextPath}/asserts/css/bootstrap.min.css”rel=”stylesheet”><link href=”${pageContext.request.contextPath}/asserts/css/signin.css”rel=”stylesheet”></head><body class=”text-center”><form class=”form-signin” action=”${pageContext.request.contextPath}/login”><img class=”mb-4″src=”${pageContext.request.contextPath}/asserts/img/logo.jpg” alt=””width=”72″ height=”72″><h3 class=”h3 mb-3 font-weight-normal”>统一登录中心</h3><input type=”text” class=”form-control” placeholder=”Username”name=”username” required=”” autofocus=””><input type=”password” class=”form-control” placeholder=”Password”name=”password” required=””><div class=”checkbox mb-3″><label><input type=”checkbox” value=”remember-me”> 记住我 </label></div><button class=”btn btn-lg btn-primary btn-block” type=”submit”>登录</button><p class=”mt-5 mb-3 text-muted”>艾编程教育</p></form></body></html>

3. 实现登录请求

package com.icoding.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class SsoServerController {@RequestMapping(“/index”)public String index(){return “login”;}@RequestMapping(“/login”)@ResponseBodypublic String login(String username,String password){System.out.println(“Debug: username=>” username ” |password=>” password);return “Login ok”;}}

4. 配置Tomcat,8001端口,发布测试!登录,及跳转OK

2.2.3、客户端-1:淘宝

1. 编写一个淘宝首页的内容页

<%@ page contentType=”text/html;charset=UTF-8″ language=”java” %><html><head><title>淘宝首页</title></head><body><h1>淘宝首页</h1><p>当前登录用户:<span>admin</span> <br><%–注销应该去SSO中获取–%><a href=””>注销</a></p></body></html>

2. 编写一个跳转的Controller

package com.icoding.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controllerpublic class LoginController {@RequestMapping(“/taobao”)public String index(){return “taobao”;}}

3. 配置tomcat,8002,启动测试!http://localhost:8002/taobao

思考问题?假设当前没登录,是不应该进入主页的,我们需要加一个权限验证,如果没登录应该跳转到统一的登录中心先登录,一般可以使用拦截器或者过滤器实现!

1. 导入素材准备好的工具类!

2. 编写一个拦截器

package com.icoding.interceptor;import com.icoding.utils.SSOClientUtil;import org.springframework.util.StringUtils;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.util.HashMap;import java.util.Map;public class SsoClientInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponseresponse, Object handler) throws Exception {//1. 判断是否存在会话HttpSession session = request.getSession();Boolean isLogin = (Boolean) session.getAttribute(“isLogin”);if (isLogin!=null && isLogin){ //存在会话,直接放行!return true;}//没有会话,跳转到统一认证中心!检查是否有其他的系统登录过SSOClientUtil.redirectToSSOURL(request,response);return false;}}

3. 配置拦截器,拦截所有的请求

<!–关于拦截器的配置–><mvc:interceptors><mvc:interceptor><mvc:mapping path=”/**”/><!–bean配置的就是拦截器–><bean class=”com.icoding.interceptor.SsoClientInterceptor”/></mvc:interceptor></mvc:interceptors>

4. 测试,看是否能够跳转!启动两个tomcat!url跳转OK,我们需要去服务器处理这个请求!

服务器端登录检查判断

1. 编写controller

@RequestMapping(“/checkLogin”)public String checkLogin(String redirectUrl, HttpSession session, Modelmodel){//1. 判断是否有全局的会话String token = (String) session.getAttribute(“token”);if (StringUtils.isEmpty(token)){//没有全局会话,跳转到统一认证中心的登录界面model.addAttribute(“redirectUrl”,redirectUrl);return “login”;}else {//有全局会话,取出令牌信息,重定向到 redirectUrl , 带上令牌model.addAttribute(“token”,token);return “redirect:” redirectUrl;}}

2. login页面增加一个隐藏域

<input type=”hidden” name=”redirectUrl” value=”${redirectUrl}”>

3. 测试,是否能够成功跳转!

编写登录的controller

1. 模拟一个存放用户令牌信息的数据库

package com.icoding.db;import java.util.HashSet;import java.util.Set;public class MockDB {public static Set<String> T_TOKEN = new HashSet<String>();}

2. 完善login方法

@RequestMapping(“/login”)public String login(String username,String password,String redirectUrl,Modelmodel,HttpSession session){System.out.println(“Debug: username=>” username ” |password=>” password);//模拟数据if (“admin”.equals(username) && “123456”.equals(password)){//校验成功!//1. 创建令牌信息String token = UUID.randomUUID().toString();System.out.println(“tocken创建成功=>” token);//2. 创建一个全局的会话,把令牌信息放入到会话中!session.setAttribute(“token”,token);//3. 需要把令牌信息放入到数据库中。MockDB.T_TOKEN.add(token);//4. 重定向到 redirectUrl,并且把令牌带上model.addAttribute(“tocken”,token);return “redirect:” redirectUrl;}//如果密码不正确,重新回到登录页面System.out.println(“用户名密码不正确,重新回到登录页面”);model.addAttribute(“redirectUrl”,redirectUrl);return “login”;}

3. 我们需要在客户端判断这个令牌!在拦截器中实现!

4. 服务端接收客户端的tocken验证,返回结果,需要在数据库中匹配!

//校验token是否是认证中心产生的@RequestMapping(“/verify”)@ResponseBodypublic String verifyToken(String token){if (MockDB.T_TOKEN.contains(token)){System.out.println(“服务端token验证OK=>” token);return “true”;}return “false”;}

5. 我们启动两个项目进行测试!

访问 http://www.tb.com:8002/taobao ,会被弹到SSO服务

登录,登录成功,跳转回http://www.tb.com:8002/taobao

观察Tocken输出!搞定

2.2.4、客户端-2:天猫

1. 我们新建一个module sso-client-tm

2. 将 tb 的东西全部拷贝过去!

3. 修改taobao首页为tianmao首页

4. 修改controller跳转请求

@RequestMapping(“/tianmao”)public String index(){return “tianmao”;}

5. 修改 sso.properties 配置

server-url-prefix=http://www.sso.com:8001client-host-url=http://www.tm.com:8003

6. 配置Tomcat , 8003端口,启动测试访问!

我们目前的状态,两个客户端,一个服务器,我们在其中一个登陆,看一下另一个的状态!

单点登录,搞定!

2.2.5、单点登出实现

单点登出手写实现及CAS 演示

单点登录,当然也需要单点注销,也就是在一个子系统中注销,在整个子系统中都会注销!

我们来编写一下!

1. 模拟在服务端增加一个seession登出的表增加一个vo对象

package com.icoding.vo;import lombok.Data;@Datapublic class ClientInfoVo {private String clientUrl;private String jessionid;}

使用map和list模拟表结构

public class MockDB {public static Set<String> T_TOKEN = new HashSet<String>();public static Map<String,List<ClientInfoVo>> T_CLIENT_INFO = newHashMap<String, List<ClientInfoVo>>();}

2. 客户端拦截器校验token时候携带上 clientUrl 和 jessionid 参数,两个客户端的都需要添加!

params.put(“clientUrl”,SSOClientUtil.getClientLogOutUrl()); //logOutparams.put(“jessionid”,session.getId()); //jessionid

3. 接下来,就需要去服务器端修改 我们的验证方法:verify

//校验token是否是认证中心产生的@RequestMapping(“/verify”)@ResponseBodypublic String verifyToken(String token,String clientUrl,String jessionid){if (MockDB.T_TOKEN.contains(token)){System.out.println(“服务端token验证OK=>” token);//校验token是否是认证中心产生的@RequestMapping(“/verify”)@ResponseBodypublic String verifyToken(String token,String clientUrl,String jessionid){if (MockDB.T_TOKEN.contains(token)){System.out.println(“服务端token验证OK=>” token);

4. 接来下我们就可以去实现在页面中实现注销功能了!本质调用的是 sso 服务器的登出方法!

@RequestMapping(“/tianmao”)public String index(Model model){model.addAttribute(“serverLogOutUrl”,SSOClientUtil.getServerLogOutUrl());return “tianmao”;}

<%–注销应该去SSO中获取–%><a href=”${serverLogOutUrl}”>注销</a>

5. 在SSO服务端编写登出方法

@RequestMapping(“/logOut”)public String logOut(HttpSession session){//销毁全局会话session.invalidate();// 获取注册的子系统,一次调用子系统的登出方法!// 由于服务器的session可能自动失效,所以我们需要写到Session销毁监听器中return “login”;}

6. 编写session销毁监听

package com.icoding.listener;import com.icoding.db.MockDB;import com.icoding.utils.HttpUtil;import com.icoding.vo.ClientInfoVo;HttpUtil工具类新增一个重载方法:import javax.servlet.http.HttpSession;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;import java.util.List;public class MySessionListener implements HttpSessionListener {public void sessionCreated(HttpSessionEvent se) {}public void sessionDestroyed(HttpSessionEvent se) {HttpSession session = se.getSession();String token = (String) session.getAttribute(“token”);//销毁 T_CLIENT_INFO 表中的数据MockDB.T_TOKEN.remove(token);List<ClientInfoVo> clientInfoVoList =MockDB.T_CLIENT_INFO.remove(token);//获取注册的子系统,一次调用子系统的登出方法!try {for (ClientInfoVo vo : clientInfoVoList) {HttpUtil.sendHttpRequest(vo.getClientUrl(),vo.getJessionid());}} catch (Exception e) {e.printStackTrace();}}}

HttpUtil工具类新增一个重载方法:

/*** 模拟浏览器的请求*/public static void sendHttpRequest(String httpURL,String jesssionId) throwsException{//建立URL连接对象URL url = new URL(httpURL);//创建连接HttpURLConnection conn = (HttpURLConnection) url.openConnection();//设置请求的方式(需要是大写的)conn.setRequestMethod(“POST”);//设置需要输出conn.setDoOutput(true);conn.addRequestProperty(“Cookie”,”JSESSIONID=” jesssionId);//发送请求到服务器conn.connect();//这个地方有坑;File Not Found 404int code = conn.getResponseCode();if (code == 200) {conn.getInputStream(); // 得到网络返回的正确输入流} else {conn.getErrorStream(); // 得到网络返回的错误输入流}conn.disconnect();}

7. 监听器注册到 web.xml

<listener><listener-class>com.icoding.listener.MySessionListener</listener-class></listener>

8. 客户单也需要接收 logOut请求;两个都要写

@RequestMapping(“/logOut”)public void logOut(HttpSession session){session.invalidate();}

9. ok,我们可以启动几个项目来测试一下!现在为止,登录注销OK!

2.3、CAS

2.3.1、介绍

CAS(Central Authentication Service) 是 Yale 大学发起的一个开源项目,是单点登录的一种现方式HTTPS http:

分为CAS Server服务端和CAS Client客户端:

CAS Server:

CAS Server 负责完成对用户的认证工作, CAS Server 需要独立部署,有不止一种 CAS Server 的实现, Yale CAS Server 和 ESUP CAS Server 都是很不错的选择。CAS Server 会处理用户名 / 密码等凭证(Credentials) ,它可能会到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户密码,对这种方式, CAS 均提供一种灵活但同一的接口 / 实现分离的方式, CAS 究竟是用何种认证方式,跟 CAS协议是分离的,也就是,这个认证的实现细节可以自己定制和扩展.

CAS Client:

CAS Client 负责部署在客户端(注意,我是指 Web 应用),原则上, CAS Client 的部署意味着,当有对本地 Web 应用的受保护资源的访问请求,并且需要对请求方进行身份认证, Web 应用不再接受任何的用户名密码等类似的 Credentials ,而是重定向到 CAS Server进行认证。目前, CAS Client 支持(某些在完善中)非常多的客户端,包括 Java 、 .Net 、 ISAPI 、 Php 、 Perl 、 uPortal 、 Acegi 、Ruby 、VBScript 等客户端,几乎可以这样说, CAS 协议能够适合任何语言编写的客户端应用。

2.3.2、配置https

为了提高安全性,要求和CAS服务器的所有通信必须发生在一个安全的通道.

步骤:

1. 生成服务器的密匙文件tomcat.keystore

2. 命令:

# 生成密钥keytool -genkey -alias icoding -keyalg RSA -keysize 2048 -validity 36500 -keystore C:/keys/icoding.keystore

3. 参数说明:

-genkey 生成密钥-alias 指定别名,这里是icoding-keyalg 指定密钥算法,这时指定RSA,-keysize 指定密钥长度,默认是1024位,这里指定2048,长一点,比较难破解,-validity 指定证书有效期,这里指定36500天.-keystore 指定密钥库存储位置,这里存在 D:/keys/目录下

注意:您的名字与姓氏www.sso.com是CAS服务器使用的域名,不是随便乱定的,其他的随意.

4. 导出证书

keytool -export -alias icoding -storepass 123456 -file C:/keys/icoding.cer -keystore C:/keys/icoding.keystore

5. 参数说明

-alias指定别名为icoding ;-storepass指定私钥为123456;-file指定导出证书的文件名为icoding.cer;-keystore指定之前生成的密钥文件的文件名。注意:-alias和-storepass必须为生成icoding .keystore密钥文件时所指定的别名和密码,否则证书导出失败.

6. 导入证书导入到jdk信任库

keytool -import -alias icoding -keystoreC:/Progra~1/Java/jre1.8.0_181/lib/security/cacerts -file C:/keys/icoding.cer-trustcacerts#注意:# 1.原来的$JAVA_HOME/jre/lib/security/cacerts文件要先删掉,否则会报出# java.io.IOException: Keystore was tampered with, or password was incorrect错误.# 2.如果路径有空格或特殊字符,加上引号.

2.3.3、使用cas-overlay-template搭建cas服务器

什么是maven的overlay?

overlay可以把多个项目war合并成为一个项目,并且如果项目存在同名文件,那么主项目中的文件将覆盖掉其他项目的同名文件。

apereo提供了一个基于层结构的框架,可以帮助开发者快速引入cas server的代码,然后实现自由配置或代码覆盖,打包方式也非常简单。

选择对应的版本,比如:5.2,下载或者clone!

我们将它克隆到本地:

环境要求:JDB1.8

下载或者克隆cas-overlay-template(version:5.2.3)项目到本地,用Intellji Idea打开项目,下载依赖时间较长,需耐心等待,建议使用国内的镜像.有个依赖下载很慢或者下载不了cas-server-webapp-tomcat,建议直接下载之后放到本地仓库对应的目录会快点.

http://mvnrepository.com/artifact/org.apereo.cas/cas-server-webapp-tomcat1.

1、将这个项目使用IDEA 导入打开!等待其导入完毕!

2、可以把pom.xml 文件中,没用和爆红的配置删掉!

3、在项目中添加src/main/java和src/main/resources目录,并将src/main/java设置为代码文件根目录,将src/main/resources设置为资源文件根目录。

4、将overlays目录下的WEB-INF/classes/目录中的application.properties文件复制到

src/main/resources中

5、将C:/keys/icoding.keystore拷贝到resources目录,修改配置

6. 增加一个静态的用户,在配置文件的最下面!

### CAS Authentication Credentials#cas.authn.accept.users=admin::123456

7. 打开命令行,进入项目所在目录,运行内置的命令: build.cmd run ,执行时间较长,请耐心等待

8. 如果出现如下界面说明CAS Server服务已经启动

说明:

CAS is confifigured to accept a static list of credentials for authentication. While this is

generally useful for demo purposes, it is STRONGLY recommended that

这个只是警告,不是错误,意思是现在使用的是静态的验证,不够安全.没关系,我们后续会换成数据库的验证.

9. 在浏览器中输入https://www.sso.com:8443/cas/login 出现如下界面说明CAS Server服务搭建成功.

注意:www.sso.com是我们自己在host文件中配置的映射,映射到127.0.0.1.

默认登陆账号密码是 casuser:Mellon

可以在application.properties中最后一行修改cas.authn.accept.users=admin::123456

10. 可以登录测试一下!

2.3.4、服务端连接数据库

1. 创建数据库cas,新增一张用户表sys_user

create database `cas`;USE `cas`;SET FOREIGN_KEY_CHECKS=0;– —————————— Table structure for sys_user– —————————-DROP TABLE IF EXISTS `sys_user`;CREATE TABLE `sys_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255) DEFAULT NULL,`password` varchar(255) DEFAULT NULL,`expired` int(11) DEFAULT NULL,`disabled` int(11) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;– —————————— Records of sys_user– —————————-INSERT INTO `sys_user` VALUES (‘1’, ‘admin’,’21232f297a57a5a743894a0e4a801fc3′, ‘0’, ‘1’);INSERT INTO `sys_user` VALUES (‘2’, ‘kuangshen’,’fae0b27c451c728867a567e8c1bb4e53′, ‘1’, ‘0’);INSERT INTO `sys_user` VALUES (‘3’, ‘icoding’,’fae0b27c451c728867a567e8c1bb4e53′, ‘0’, ‘0’);

说明:

expired字段表示过期,1表示已过期,需要修改密码

disabled表示账号是否禁用,1表示禁用

如果采用MD5加密,那password就是MD5加密后的密文,sha同样如此

admin、kuangshen、icoding明文密码分别是admin、666、666

2. 在POM文件中添加数据库的依赖,偷懒了,直接使用JDBC

<dependencies><!–新增支持jdbc验证–><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-support-jdbc</artifactId><version>${cas.version}</version></dependency><!–使用mysql驱动–><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency></dependencies>

3. 在application.properties新增如下配置.我们此时可以把静态的账号注释掉了,一会使用数据库中的

### CAS Authentication Credentials## cas.authn.accept.users=admin::123456#jdbc验证配置#Query Database Authentication 数据库查询校验用户名开始#查询账号密码sql,必须包含密码字段cas.authn.jdbc.query[0].sql=select * from sys_user where username=?#指定上面的sql查询字段名(必须)cas.authn.jdbc.query[0].fieldPassword=password#指定过期字段,1为过期,若过期需要修改密码cas.authn.jdbc.query[0].fieldExpired=expired#为不可用字段段,1为不可用,cas.authn.jdbc.query[0].fieldDisabled=disabled#数据库方言mysql的知识cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect#数据库驱动cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver#数据库连接cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8#数据库用户名cas.authn.jdbc.query[0].user=root#数据库密码cas.authn.jdbc.query[0].password=123456#默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULTcas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5#Query Database Authentication 数据库查询校验用户名结束 #jdbc验证配置

4. 说明

以上配置,如驱动,查询数据库等等需要根据不同的场景进行调整-若密码无加密,调整passwordEncoder.type=NONE-若密码加密策略为SHA,调整passwordEncoder.encodingAlgorithm=SHA-若算法为自定义,实现org.springframework.security.crypto.password.PasswordEncoder接口,并且把类名配置在passwordEncoder.type

5. 命令行启动测试

build.cmd run

6. 测试三个账户看看情况

01. admin-admin

02. kuangshen-666

03. icoding-666

7. 使用JDBC进行认证就完成了!

2.3.5、普通客户端集成CAS

1. 下载之后直接导入到 IDEA 中

3. 在pom.xml 将 jetty 的配置删掉,加入tomcat的插件

<plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><port>8010</port><path>/</path></configuration></plugin>

4. 启动客户端访问:

5. 我们目前的客户端访问过去是 http 的,服务器只支持 https,所以我们需要在服务器端增加 http的支持,首先,我们需要复制 services 的配置,增加http的支持

然后,在application.properties文件中添加:

cas.tgc.secure=falsecas.serviceRegistry.initFromJson=true

这两个配置完后,服务端就支持http的协议了,

6. 我们将两个项目启动下,然后测试

www.tb.com:8010/index.jsp

发现会跳转到统一的认证中心

https://www.sso.com:8443/cas/loginservice=http://www.tb.com:8010/index.jsp

我们登录试试 icoding – 666 , 成功页面,显示了登录人的名字

7. 我们将 tb 项目复制一份,改为 tm 项目,然后再次测试,两个客户端联调主要修改客户端端口号即可!

8. 重新启动测试,看是否能够实现一处登陆,处处登录;

10. 注销成功后,再次查看 我们的应用,发现两个都已经被注销掉了

2.3.6、SpringBoot集成CAS

1. 新建一个springboot的项目 cas-springboot

3. 添加pom.xml 依赖

<dependency><groupId>net.unicon.cas</groupId><artifactId>cas-client-autoconfig-support</artifactId><version>2.3.0-GA</version></dependency>

4. 在应用启动入口类上添加配置@EnableCasClient

@SpringBootApplication@EnableCasClient //开启支持public class CasSpringbootApplication {public static void main(String[] args) {SpringApplication.run(CasSpringbootApplication.class, args);}}

5. 在应用的配置文件中(application.properties)添加CAS的配置:

6. 然后我们只需要启动项目即可!十分的简单!

发表评论

登录后才能评论