一,概述
二,会话技术
会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术
2.1 Cookie
Cookie原理:在浏览器中,Cookie 是服务器让浏览器帮忙携带信息的手段,就像饼干里的纸条,浏览器会储存它,并且在后续的 HTTP 请求中再次发送给服务器。
因为Cookie是Http协议支持的技术
-
服务端通过响应头里面设置Set-Cookie值来将cookie传递给浏览器。
-
浏览器通过请求头里面的Cookie将cookie值传递给服务端。
跨域区分三个维度:协议、IP地址、域名、端口。只要三个中有任何一个不同,这次请求就是跨域请求。如下图
测试代码:
@GetMapping("/c1")
public void cookie1(HttpServletResponse response){
response.addCookie(new Cookie("userName","zhangsan"));
}
//获取客户端发送过来的Cookie
@GetMapping("/c2")
public void cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
System.out.println(cookie.getName()+":"+cookie.getValue());
}
}
2.2 Session
**Session原理:**存在服务器的一种用来存放用户数据的类哈希表结构
-
当浏览器第一次发送请求的时候服务器会生成一个hashtable和一个sessionid,sessionid来唯一标识这个hashtable,响应的时候会通过一个响应头set-cookie返回给浏览器,浏览器再将这个sessionid存储在一个名为JESSIONID的cookie中。
-
浏览器在发送第二次请求时,就会带上这个cookie,这个cookie会存储sessionid一并发]送给了一个服务,服务器再从请求中提取出对应的一个sessionid并和当前保存的所有的一个sessionid去进行一个对比然后找到这个sessionid对应的用户信息。
测试代码:
//往HttpSession中存储值
@GetMapping("/s1")
public void session1(HttpSession session){
log.info("HttpSession-s1:{}",session.hashCode());
session.setAttribute("Username","tom");//往session中存储数据
}
//从HttpSession中获取值
@GetMapping("/s2")
public void session2(HttpServletRequest request){
HttpSession session = request.getSession();
log.info("HttpSession-s2:{}",session.hashCode());
Object username = session.getAttribute("Username");//从session中获取数据
log.info("username:{}",username);
}
Cookie是信息存浏览器,Session是将信息存服务端,然后浏览器存sessionId
2.3 令牌token
**令牌原理:**令牌就是用户身份的标识,本质是一串字符串。
- 当用户登录成功后,服务端会生成一个令牌,将令牌响应给浏览器。
- 浏览器接收到令牌后将令牌存储起来,在后续每一次请求中,都将这个令牌携带到服务端。
- 服务端接收到令牌后会校验这个令牌的有效性,如果令牌有效代表用户成功登录了,无效代表用户未登录。
- 在同一次会话的多次请求之间将共享的数据存储在令牌之中。
2.3.1 JWT令牌介绍
数据由JSON格式变成字符串是通过Base64编码而成的。
- Base64: 是一种基于64个可打印字符 (A-Z a-z 0-9 + /)来表示二进制数据的编码方式
2.3.2 JWT令牌的生成
-
引入jwt令牌的依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> -
生成jwt令牌
@Test public void genJwt(){ Map<String,Object> claims=new HashMap<>(); claims.put("id",1); claims.put("name","tom"); String jwt = Jwts.builder() .signWith(SignatureAlgorithm.HS256, "admin")//指定签名算法和密钥 .setClaims(claims)//自定义内容(载荷部分 .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))//设置jwt令牌有效期 .compact();//封装结尾 System.out.println(jwt); }注意要点:
-
jwt令牌算法密钥长度最低为4个字符
-
高版本jdk会存在找不到包的情况,手动导入
<dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency>
-
2.3.3 JWT令牌的解析
@Test
public void parseJwt(){
String jwt="eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY5OTA4MzgyOX0.1p8c09OXoy7Gkrx_hGTbAZ3Ws3HKvlde3Nfd7VetlPM";
Claims body= Jwts.parser()
.setSigningKey("admin123")//输入jwt签名密钥
.parseClaimsJws(jwt)//输入jwt字符串
.getBody();//获取jwt的数据载荷部分内容。
System.out.println(body);
}
- JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
- 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法
2.3.4 下发JWT令牌
后端只需要将登录成功后生成的jwt令牌字符串响应给浏览器即可,前端需要将接受到的jwt字符串在之后的每一次请求头header中携带jwt字符串到服务端,请求头的名字为token。
三,统一拦截处理
3.1 Filter过滤器
快速入门:
详解:
-
Filter执行流程:
-
Filter的拦截路径
-
过滤器链
一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链
顺序:注解配置的Filter,优先级是按照过滤器类名字符串的自然排序
3.2 Interceptor拦截器
Interceptor快速入门:
拦截器Interceptor详解:
-
拦截路径
-
拦截器可以根据需求,配置不同的拦截路径
-
-
-
执行流程
3.3 Filter和Interceptor区别
接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现Handlerlnterceptor接口。
拦截范围不同:过滤器Filter会拦截所有的资源,而interceptor只会拦截Spring环境中的资源。