1、登录功能
登录功能的核心是:接收前端请求传递的用户名和密码 ,再根据用户名查询密码并且和前端传递的密码做以比较,如果相同,则说明用户输入的用户名和密码正确。如果出现查询不到用户名或密码不一致,则说明用户输入的用户名或密码错误。
2、登录校验
2.1、问题分析
但是此做法会出现一个问题我们在浏览器直接输入别的页面网址,页面可以直接进来,登录功能就没有达到我们想要的效果,所以我们需要去添加登录校验,如下图。
看完以上我们在宏观上对登录有一个认知了,众所周知http协议是无状态的,所谓无状态指的是每次请求都是的,下一次请求并不会携带上次请求的数据,在实现登录以后,接下来操作中,服务器也不知道用户到底登录了没有,因为http请求是无状态的,所以无法判断,所以我们要采取在用户登录后存储一个标识,在接下来接口方法执行之前,先判断用户是否登录,如果没有登录,返回前端让重新登录就好了。
具体应该怎么来实现呢?一共涉及到web开发中的两个技术:
1、会话技术
(1)cookie
(2)session
(3)令牌技术(主要讲解)
2、统一拦截技术
(1)Servlet规范中的Filter过滤器
(2)Spring提供的interceptor
下面我们先学习会话技术。
2.2、会话技术
2.2.1、会话技术介绍
会话字面意思就是谈话、交谈,在web开发中,浏览器与服务器之间的一次连接我们就称为一次会话。
举个例子,我们打开浏览器访问服务器资源(浏览器不能关,服务器不能断)
第一次:访问登录接口
第二次:访问药品接口
第三次:访问员工接口
只要浏览器不关,以上三次请求属于一次会话中完成的。
听完这些我们对会话技术有了一定的了解,接下来我们在了解一下会话跟踪。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪技术有两种:
1、Cookie(客户端会话跟踪技术)
数据存储在客户端浏览器
2、Session(服务端会话跟踪技术)
数据存储在服务端
2.2.2、会话跟踪方案
2.2.2.1、Cookie
Cookie 是客户端会话跟踪技术,它是存储在客户端浏览器的,我们使用 Cookie 来跟踪会话,我们就可以在浏览器第一次发起请求来请求服务器的时候,我们在服务器端来设置一个Cookie。
用户登录厚设置一个Cookie,我们在Cookie中存一些数据。服务器端在给客户端在响应数据的时候,会自动的将 Cookie 响应给浏览器,浏览器接收到响应回来的 Cookie 之后,会自动的将 Cookie 的值存储在浏览器本地。接下来在后续的每一次请求当中,都会将浏览器本地所存储的 Cookie 自动地携带到服务端。
优缺点
优点:http协议中支持的技术,Set-Cookie 响应头的解析和cookie数据的携带,都是浏览器自动的,无需手动操作
缺点:
1、移动端APP无法使用
2、不安全、用户可以在浏览器自己禁用Cookie
3、Cookie不能跨域
2.2.2.2、Session
Session是服务器端会话跟踪技术,所以它是存储在服务器端的,Session 的底层其实就是基于我们刚才所介绍的 Cookie 来实现的。
浏览器在第一次请求服务器时,我们直接在服务器中回去会话对象Session,第一次请求Session,会话对象不存在,这时服务器会自动创建一个session,每一个Session都有一个ID,在接下来服务器响应浏览器时,将session的id通过cookie响应给浏览器,相当于在响应头中增加了一个Set-Cookie响应头,Cookie的名字是固定的JSESSIONID代表服务器端会话对象的Session的ID,浏览器会自动识别响应头,然后将Cookie存储在浏览器本地。接下来没词请求中,都会将Cookie数据获取出来并携带到服务端接下来服务器拿到JSESSIONID这个 Cookie 的值,也就是 Session 的ID。拿到 ID 之后,就会从众多的 Session 当中来找到当前请求对应的会话对象Session。
优缺点
优点:Session是存储在服务端的,安全
缺点:
1、服务器集群环境下无法直接使用Session
2、移动端APP中无法使用Cookie
3、用户可以自己禁用Cookie
4、Cookie不能跨域
以上两种传统会话技术存在很多问题,,为了解决以上问题,在现在企业级开发当中,我们都会使用第三种方案,令牌技术。
2.3、令牌技术
2.3.1、介绍
令牌技术其本质就是一个字符串。令牌有很多种,我们使用的是JWT令牌。
JWT全称:JSON Web Token (官网:https://jwt.io/)
JWT的组成: (JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)
第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
JWT令牌最典型的应用场景就是登录认证:
1. 在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。
2. 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。
3. 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。
JWT在整个流程中涉及到两部操作,分别是生成和校验。
2.3.2、生成和校验
首先我们先来实现JWT令牌的生成。要想使用JWT令牌,需要先引入JWT的依赖:
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
生成JWT代码实现:
/**
* 生成jwt
* 使用Hs256算法, 私匙使用固定秘钥
* @param secretKey jwt秘钥
* @param ttlMillis jwt过期时间(毫秒)
* @param claims 设置的信息
* @return
*/
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
// 指定签名的时候使用的签名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成JWT的时间
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
// 设置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
// 设置过期时间
.setExpiration(exp);
return builder.compact();
}
实现了JWT令牌的生成,下面我们接着使用Java代码来校验JWT令牌(解析生成的令牌):
/**
* Token解密
*
* @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
* @param token 加密后的token
* @return
*/
public static Claims parseJWT(String secretKey, String token) {
// 得到DefaultJwtParser
Claims claims = Jwts.parser()
// 设置签名的秘钥
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 设置需要解析的jwt
.parseClaimsJws(token)
.getBody();
return claims;
}
我们在使用JWT令牌时需要注意:
JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
如果JWT令牌解析校验时报错,则说明 JWT令牌被篡改 或 失效了,令牌非法。
令牌技术进行会话跟踪:
在登录时,如果登录成功,我们就可以生成一个令牌,令牌就是用户的合法身份凭证。接下来我们在响应数据的时候,我们就可以直接将令牌响应给前端。在前端程序当中接收到令牌之后,就需要将这个令牌存储起来。这个存储可以存储在 cookie 当中,也可以存储在其他的存储空间(比如:localStorage)当中。最后在后续的每一次请求当中,都需要将令牌携带到服务端。携带到服务端之后,接下来我们就需要来校验令牌的有效性。如果令牌是有效的,就说明用户已经执行了登录操作,如果令牌是无效的,就说明用户之前并未执行登录操作。
此时,如果是在同一次会话的多次请求之间,我们想共享数据,我们就可以将共享的数据存储在令牌当中就可以了。
优缺点
优点:
1、支持pc端、移动端
2、解决集群环境下的认证问题
3、减轻服务器的存储压力
缺点:需要自己实现(包括令牌的生成、令牌的传递、令牌的校验)
现在企业开发当中使用的最多的就是令牌技术进行会话跟踪。而前面的这两种传统的方案,现在企业项目开发当中已经很少使用了。