目录
2.3、springboot的单体项目使用传统session实现登录和认证
3、Cookie-Session认证升级版----redis实现session共享
JWT简称JSON Web Token,也就是通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
单点登录:
单点登录的英文名叫做:Single Sign On(简称SSO)。在以前的时候,一般我们就单系统,所有的功能都在同一个系统上。后来,我们为了合理利用资源和降低耦合性,于是把单系统拆分成多个子系统。
比如阿里系的淘宝和天猫,很明显地我们可以知道这是两个系统,但是你在使用的时候,登录了天猫,淘宝也会自动登录。简单来说,单点登录就是在多个系统中,用户只需一次登录,各个系统即可感知该用户已经登录。

1、客户端向服务端发送账号密码进行认证。
2、服务端在校验账号密码正确之后,将当前用户的基本信息保存到当前会话(session)中,并将sessionid(JESSIONID)返回给客户端。
3、客户端在拿到sessionid之后将其保存到cookie中,并在以后的每次请求中cookie都携带该sessionid。sessionid相当于key,session相当于value,服务器通过sessionid来获取session
4、服务端根据客户端传过来的sessionid对当前用户进行认证,判断是否合法。
1、Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
2、扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。而且如果你后端应用是多节点部署。那么就需要实现session共享机制。 不方便集群应用。
3、CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击
4、Cookie-Session 只能在 web 场景下使用,如果是 APP 呢,APP 可没有地方存 cookie。
现在的产品基本上都同时提供 web 端和 APP 两种使用方式,有的产品甚至只有 APP
登录:
- //登录表单发送请求
- @PostMapping("/login")
- public String login(User user, HttpSession session,Model model){
- //省略从数据库查询
- if("luo".equals(user.getUsername())&&"123456".equals(user.getPassword()))
- {
- session.setAttribute("loginUser",user);
- return "redirect:/main.html";//重定向到main页面
- }else {
- model.addAttribute("msg","账号或密码错误");
- return "login";
- }
- }
登录成功后会将sessionid返回给客户端浏览器,存储在cookie中。
拦截器:
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- //登录检查逻辑 通过sessionid来获取session
- HttpSession session = request.getSession();
- //获取session中存储的对象
- Object loginUser = session.getAttribute("loginUser");
- if(loginUser!=null)//如果不为空,说明已经有用户登录
- return true;//登录 放行
- //拦截住,未登录 跳转到登录页面
- request.setAttribute("msg","请先登录!");
- request.getRequestDispatcher("/login").forward(request,response);
- return false;
- }
每次前端发起请求后,都会先经过这个拦截器。通过前端携带sessionid,然后通过sessionid获取对应的session。然后获取session中存储的对象
测试接口:
- @ResponseBody
- @GetMapping("/list")
- public List<User> list(){
- //查询所有数据
- List<User> list = userService.list();
- return list;
- }
如果没有登录请求其他接口会被拦截:例如list接口

登录后再次访问list接口:
先登录:

登录成功后会返回一个JESSIONID到cookie里。
再次访问list接口:

由于传统的 Cookie-Session 认证存在诸多问题,那可以把上面的方案改造一下。
1、改造 Cookie 既然 Cookie 不能在 APP 等非浏览器中使用,那就不用 cookie 做客户端存储,改用其他方式。
web 中可以使用 local storage,APP 中使用客户端数据库,这样既能这样就实现了跨域,并且避免了 CSRF 。
2、服务端也不存 Session 了,把 Session 信息拿出来存到 Redis中,这样即提高了速度,又避免了 Session 同步问题;
经过改造之后变成了如下的认证过程:
下面两张图分别演示了首次登录和非首次登录的过程。


解决了传统 Cookie-Session 方式存在的问题。这种改造需要开发者在项目中自行完成。改造起来肯定是费时费力的,而且还有可能存在漏洞。

token中可以存储用户信息,不需要像session那样将用户信息存储在服务器上,占用内存。
1、首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探
2、后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。包含用户信息,一起返回给前端,这样就不需要和session一样将用户信息存储在服务器端,就不会占用服务器的内存。
3、后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
4、前端在每次请求时将JWT放入HTTP Header中的Authorization位。(放在Header中可以解决XSS和XSRF问题)
5、后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。
6、验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。
1、简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
2、自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
3、因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持
4、不需要在服务端保存会话信息,特别适用于分布式微服务