【手摸手,带你搭建前后端分离商城系统】03 整合Spring Security token 实现方案,完成主业务登录
上节里面,我们已经将基本的前端 VUE + Element UI 整合到了一起。并且通过 axios 发送请求到后端API。
解决跨域问题后、成功从后端获取到数据。
本小结,将和大家一起搭建 Spring-Security + token 的方式先完成登录。权限将在后面讲解。
引入在之前,我们的 API 都是一种裸奔的方式。谁都可以访问,肯定是不安全的。所以我们要引入安全校验框架。
传统 session 方案传统session 的方式是,通过一个 拦截器 拦截所有的请求,若 cookie 当中存储的 session id 在服务端过期后、则要求前端重新登录,进而获取一个新的session
session 与 cookie 区别因为HTTP 是一种无状态的协议。所以服务端不知道这个 请求是谁发过来的,有好多人访问服务器,但是对于服务器来说,这些人我都不认识。就需要一种东西来给每个人加一个 ID 。
session(会话) 是一种客户端发起请求后, 服务端用来识别用户的东西,可以保存一些用户的基本信息。比如ID什么的
cookie 是一种客户端浏览器用来记录和保存信息的东西。简单理解,如图所示。
当然,默认的cookie 里面总会包含一串 JSESSIONID
session认证所显露的问题Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
JWThttps://jwt.io/
肯定是原有的session认证的方式存在弊端、我们就需要采取一种新的方式来进行验证。JWT
JWT token 由三部分构成:
头部(header)
载荷(playload)
签证(signature)
具体的内容可以参考: https://www.jianshu.com/p/576dbf44b2ae
头部 header头部一般包含加密算法和类型。例如
{ "alg": "HS256",// 加密算法 "typ": "JWT" // 声明类型 } 负荷 playload负载可以理解为存放信息的位置,例如:
{ "iss":"mall-pro", // 签发者 "sub":"admin", // 面向的用户 "iat": 1602737566890,//签发时间 "exp": 1602739566890//过期时间,必须大于签发时间 } 签证(signature)签证一般是头部和负荷组成内容的,一旦头部和负荷内容被篡改,验签的时候也将无法通过。
//secret为加密算法的密钥 String signature = HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)我们来参考一个生成的 JWT 实例
注意,我这里使用回车、一般三部分都是通过标点进行分割的。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 实现原理用户调用登录接口后、验证用户名和密码。验证成功后、颁发给其token
前台获得 token 后,将其存放到本地、每次的请求都将这个token 携带到请求头里面。
后台收到请求后、验证请求头里面的 Authorization 是否正确、从而判断是否可以调用这个接口。
通过解析 token 将账号信息存入 userDetail 让其顺利调用接口信息、并可以在接口中获得当前登录人的账号信息。
Spring Security安全框架,我们这里考虑使用 Spring-Security ,使用全家桶系列,一般大家都会想到apache shiro 等权限框架、都是可以的。我们这里介绍如何加入 Spring-Security
引入到 mall-security 并且添加一个配置文件。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 添加一个登陆接口我们首先从登陆接口开始,一个最基本的 controller 接受参数。当然,用户名和密码肯定是不能为空的,校验完后交给 service
@ApiOperation("用户登录接口") @RequestMapping("login") public CommonResult login(@RequestBody @Valid @ApiParam("用户名密码") UmsAdminLoginParam param) { UmsAdminTokenBO tokenBO = umsAdminService.umsAdminLogin(param); return CommonResult.success(tokenBO); }具体的内容无非是:查询数据库、是否存在、密码是否正确。正确就构造一个 token 返回给前端。这里主要说一些重要的点。
断言与全局异常处理断言可以理解为:若当前行不符合判断条件、则抛出异常。或者直接使用断言来抛出一个异常。比如账号不存在,直接抛出一个异常即可。
全局异常处理:全局异常处理,在全局统一拦截异常信息,并通过{code=500,message="error message"} 的方式返回给前端做出提示即可。