单点登录详解(token简述)(八)
前言
为什么整理单点登录?
主要的原因还是自己以前学习的时候曾经用过,但是时间太久,忘记了里面用到了哪些技术、及如何实现的,每次想到单点登录总是感觉即会又不会,这次整理session时,又涉及到了单点登录,而且单点登录里面还用到了redis,也是我这次要整理的内容,故而单独把单点登录抽出来,系统的梳理下,以备下次忘记时重温。
顺便也把以前学习分布式项目中用到的相关技术:double、zookeeper等统一梳理一遍,加强对现在项目的整理架构的理解。
其实,无论是单一的应用,还是分布式项目乃至现在的微服务技术,其实说到底,思想都是相通的,技术也是相通,只有从底层了解始末,融汇贯通,才会对技术的整体有一个把控,不至于迷失在技术进步的潮流中。
一、什么是单点登录
SSO(Single Sign On)单点登录是实现多个系统之间统一登录的验证系统,简单来说就是:有A,B,C三个系统,在A处登录过后,再访问B系统,B系统就已经处于了登录状态,C系统也是一样。举个生活中栗子:你同时打开天猫和淘宝,都进入login界面,都要求你登录的,现在你在淘宝处登录后,直接在天猫处刷新,你会发现,你已经登录了,而且就是你在淘宝上登录的用户。说明他们实现了SSO,并且持有相同的信息。
二、为什么要用到单点登录
一次登录,多处使用。
三、单点登录的实现
3.1自己什么时候用过单点登录
培训时,做的ego项目里搭建过一个单点登录的子系统。说白了,就是类似于京东、淘宝这样的一个电商项目。因为采用的是分布式项目结构,各个模块分别是一个单独的应用,分属不同的服务器,想要实现一处登录,多处使用,肯定不能再用session了。
3.2 实现思路
我们这里的实现思路如下:
Redis+Cookie实现模拟Session功能,实现登录功能。
- 适应场景 :分布式系统中,才会使用单点登录!
- 说明:在分布式项目中,一次登录,项目之间共享登录状态。
- 思路:
- 单独有一个登录(还包含注册等)项目;
- 所有其他项目需要登录,或需要使用用户登录后状态都向登录项目进行请求。
3.3为什么不能用session与cookie实现
1、跨服务器了,所以不能用session;
2、cookie的话,是存放在浏览器端的,不安全;而且用户也可以禁用cookie(这里指的是存放在本地的cookie,不是会话cookie)。
3、放到注册中心,zookeeper:不行,如果挂了就不知道用户是否登录了。(联想到了dubbo的健壮性)
4、放到redis里,redis可以搞集群,一个宕机了也没事。
3.3 代码
1、关于代码不再进行copy,相关的具体实现已经看过代码及视频了,这里只对实现的思路及过程进行说明。
2、思路
1.用户登录 1.1 用户登录的时候,向cookie中添加了一个TT_TOKEN值。 1.2 同时,还需要向redis中添加一个user对象 key = user:token value = user对象的json字符串 2.一处登录多处使用 2.1 每个页面加载的时候,需要check。当前Cookie[TT_TOKEN]中是否有值 2.2 如果有TT_TOKEN,从redis中将对象取出来! 3.退出的时候: 3.1 删除cookie 中 TT_TOKEN 3.2 删除redis中的值!
详细的代码参看下面的部分主要代码。
3、发送请求时相关参数,可以看到有cookie信息。
4、redis存的用户信息
3.3.1 登录功能
登录的功能是在一个单独的项目ego1-passport中实现的。
具体步骤
1. 需求分析:将登录的信息存放到redis中,目的是想做sso! 1.1 登录控制器接收接收两个参数username和password 1.2 调用Dubbo服务判断用户是否登录成功. 1.3 产生Cookie,存放UUID 1.4 把用户信息放入到Redis中. 1.5 从哪里跳转到登录页面,再跳转回去那个页面. 2. 实现过程,新建一个项目ego-passport,选择war类型。 3. 导入相关的配置文件。Pom.xml,web.xml,spring相关配置文件。
详细代码可以看笔记、视频去。
1、登录方法部分代码
@Value("${redis.user.key}")
private String key;
@Override
public EgoResult login(TbUser user, HttpServletRequest request, HttpServletResponse response) {
EgoResult er = new EgoResult();
// 根据数据库查询,查询出来之后,需要放到Redis中
TbUser user2 = tbUserDubboService.selByUser(user);
if (user2!=null) {
String uuid = UUID.randomUUID().toString();
// cookieName 是根据前台js来确定。
CookieUtils.setCookie(request, response, "TT_TOKEN", uuid);
// 需要存储到redis 中 key组成是由key+uuid user:uuid user:65bedb06-9931-4fe8-9eb7-7fe1f8846d0b
// value:实体类对象登录对象
jedisPoolDaoImpl.set(key+uuid, JsonUtils.objectToJson(user2));
// 返回值是根据易购商城接口sso登录来确定
er.setMsg("OK");
er.setStatus(200);
}
return er;
}
可以看出来,在登录时,实现了如下操作:
- 先查库,看有没有这个用户;
- 用户存在的话,会在cookie里添加新值,key为token(这里命名为TT_TOKEN),value为uuid。
- 将用户信息放在redis中,key为user:uuid,value为用户对象转译的json串。
为什么会用到cookie?
如下截图,其他服务页面在访问时,会通过cookie,获取token,来校验,如果不存在token的话,会返回,跳转到登录界面。
需要理解到的是,这里的cookie是一个会话cookie。
为什么要用token?
校验用户是否登录;
通过token,从redis中获取用户信息;
说白了,token就相当于一张通票,一处登陆(相当于买了一张通票,有了它,各个分园都可以去),处处使用(各个服务器都无需再次登录,并且可以通过token去redis中获取到用户信息)。
token是什么?
在下面的延伸里有说明。其实,这里的token值,就是自己编写的一个随机uuid,然后放到了cookie中。它在这个分布式项目中就是起到了一个唯一标识并记录(存在cookie里了)用户信息的作用。是用户登录的凭证。
1、用户登录的请求及返回信息简略
3.3.2 在ego-portal中当用户登录成功后,在最上面显示用户信息
通过token,从redis中获取用户信息。
请求及返回参数示例:
3.3.3 退出功能
删除redis,同时删除cookie。
关于sso的介绍与使用,也可以参考下面的链接:https://blog.csdn.net/zhangjingao/article/details/81735041
四、延伸
4.1 复习HttpSession和Cookie
4.1.1 HttpSession
2.1 session中文名称:会话.浏览器开启到关闭的这段时间!
2.2 如何产生的?
2.2.1 在request.getSession();才能产生Session,产生完成后占用系统内存的.
2.3 Session原理.
1、创建Session的时候,服务器将生成一个唯一的sessionid然后用它生成一个关闭浏览器就会失效的cookie[JSESSIONID是cookie产生]。 2、然后再将一个与这个sessionid关联的数据项加入散列表。 例如这样一段代码:Session["UserName"]=23; 假设sessionid为123那么散列表中会追加一行 sessionid username 123 23 3、当浏览器端提交到服务器时,会通过sessionid=123去散列表中寻找属于该用户的Session信息。
4.1.2 cookie
1.1 解释:客户端存值技术。
1.1.1 存储位置:内容存放在客户端浏览器中。
1.1.2 可以存储什么类型的值:只能存储文本数据(字符串)。
1.2 Cookie流程
1.2.1 步骤1: 客户端向服务器端发送请求时,浏览器会自动携带所有能获取的Cookie内容.放在请求对象中。
1.2.2 步骤2:所有Cookie产生位置都是服务器端.在服务器端可以创建任意和key-value形式的Cookie对象.并把cookie对象放入到response对象中。
1.2.2.1 在servlet或jsp脚本中Cookie c=new Cookie(“”,””)。
1.2.2.2服务器端通过request.getCookies()获取cookie。
1.2.3 步骤3:把Cookie响应给客户端(重定向)。
1.2.4 步骤4:浏览器接收到响应后,会从响应对象中获取到Cookie内容,并把Cookie内容存储在指定位置。
1.3Cookie几个概念。
1.3.1 有效时间:
1.3.1.1 默认与Session对象有效时间相同.
1.3.1.2 手动设置Cookie有效时间,固定有效时间.不刷新
//有效时间秒 c.setMaxAge(10);
1.3.2 可访问路径问题.
1.3.2.1 设置完成后cookie只能被当前目录及子目录进行访问。
/cookie/jsp/ : cookie表示项目名称jsp表示在WebContent目录下的文件夹
c.setPath("/cookie/jsp/");
1.3.3 设置可访问域名问题。
//防止其他网站获取cookie内容 c.setDomain("localhost");
案例:
@WebServlet("/TCookie") public class TCookie extends HttpServlet { private static final long serialVersionUID = 1L; // servlet : 中核心方法:service() 方法是处理请求的method="post/get"。 // 如果请求的是get,则会有service()方法,将请求发送给doGet()方法处理,post---doPost(); @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1.创建cookie Cookie cookie = new Cookie("name", "admin"); // 设置cookie 的生命周期 // cookie.setMaxAge(10); // 设置一个访问路径 // cookie.setPath("/testcookie/jsp/"); // 设置一个域名方法 cookie.setDomain("localhost"); // 2.将cookie添加到response中 response.addCookie(cookie); // 3.将cookie发送到客户端 response.sendRedirect("index.jsp"); } }
前端:
/testcookie/jsp/index1.jsp <body> <h1>index1 ------------</h1> <!-- 取cookie --> <!-- 使用小脚本来取得cookie对象 --> <% /* 数组定义: 数据类型 [] 数组名称 = new 数据类型[长度] 基本数据类型,引用数据类型 */ Cookie [] c = request.getCookies(); /* 增强for循环 for(数据类型 变量名 : 数组/集合){} */ for(Cookie cookie:c){ /* jsp九大内置对象。谁是输出out */ out.println(cookie.getName()+"======"+cookie.getValue()+"<br/>"); } %> </body>
4.2 token
token是计算机术语:令牌,令牌是一种能够控制站点占有媒体的特殊帧,以区别数据帧及其他控制帧。token其实说的更通俗点可以叫暗号,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。基于 Token 的身份验证方法
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
1.客户端使用用户名跟密码请求登录
2.服务端收到请求,去验证用户名与密码
3.验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
4.客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
5.客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6.服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。