黑马程序员JavaWeb+AI Day12登录认证
本文摘要介绍了Web开发中的登录功能实现和会话跟踪技术。主要内容包括:1. 登录功能的后端实现,使用@RequestBody接收JSON参数;2. 三种会话跟踪技术:基于客户端的Cookie、基于服务端的Session和主流的令牌技术(JWT),分析其优缺点;3. JWT令牌的组成、生成和校验方法;4. 过滤器和拦截器的实现与区别,用于登录校验等场景。文章重点讲解了现代Web开发中常用的JWT令牌
登录功能实现
post请求,json请求参数因要传入Emp对象中的两个属性,所以要用@RequestBody注解

观察到响应参数没有类包含token参数,而emp中属性太多,直接新建一个LoginInfo


Controller层
注意路径和注解,同时方法判断返回的对象是否为空,为空则数据库中不存在

Service层

实现类中调用mapper方法中的函数需要见名知义,此时的token没有,暂时设为null

Mapper层

会话技术
会话指的就是浏览器与服务器之间的一次连接,称为一次会话,在一次会话当中,是可以包含多次请求和响应的。
可以多个浏览器和同一个服务器发起会话
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
目的就是完成在同一个会话中,多个请求之间进行共享数据。

-
Cookie(客户端会话跟踪技术):数据存储在客户端浏览器当中
-
Session(服务端会话跟踪技术):数据存储在储在服务端
-
令牌技术
Cookie(过时)
客户端对服务器发起请求在服务器端会自动生成一个cookie,响应回来的 cookie 之后,会自动的将 cookie 的值存储在浏览器本地。
接下来在后续的每一次请求当中,都会将浏览器本地所存储的 cookie 自动地携带到服务端就可以实现认证
在服务端获取到 cookie 的值。判断一下这个 cookie 的值是否存在,存在就是登录过,不存在就没有,这样我们就可以基于 cookie 在同一次会话的不同请求之间来共享数据

用HttpServletResponse中的addCookie来设置cookie

获取cookie用HttpServletRequest的getCookies()

响应头 Set-Cookie :设置Cookie数据的(KEY = VALUE的形式)
请求头 Cookie:携带Cookie数据的
在响应头中可以查看set cookie
![]()
之后会存储在浏览器

在之后发送请求中会携带请求头cookie到服务端

优缺点:
-
优点:HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)
-
缺点:
-
移动端APP(Android、IOS)中无法使用Cookie
-
不安全,用户可以自己禁用Cookie
-
Cookie不能跨域()
-
跨域的维度(三个维度有任何一个维度不同,那就是跨域操作):
-
协议(HTTP/HTTPS)
-
IP/协议(域名不同)
-
端口(8080和90不同)
-

Session(过时)
底层原理是Cookie,在请求和响应时传递的是JSESSIONID,也就是浏览器在第一次发送请求给后端时,服务器会生成一个Session然后吧Session的ID传回给浏览器作为标识
这里的响应头也是Set-Cookie,cookie 的名字是固定的 JSESSIONID
在后面的所有请求中都会携带这个ID,从众多Cookie中看是否存在这个ID,就可以实现认证

设置Session用HtttpSession的setAttribute方法
setAttribute(name, value)

获取Session用HttpServletRequest的getAttribute()
getAttribute(name)

在响应头中的格式是JSESIIONID = (系统创建的sessionID)
setAttribute(name, value)中的name和value是在服务器内存中这样存在的
{JSESSIONID_ABC123DEF456: {"loginUser": "tom"}}

在请求头中依旧是JSESIIONID = (系统创建的sessionID)
![]()
在第一次之后的每次请求都会把cookie带到服务器进行认证
优缺点
-
优点:Session是存储在服务端的,安全
-
缺点:
-
服务器集群环境下无法直接使用Session
-
移动端APP(Android、IOS)中无法使用Cookie
-
用户可以自己禁用Cookie
-
Cookie不能跨域
-
后三点都是Cookie的缺点
令牌【主流】
请求头:token
在用户登录时可以生成一个令牌,然后把令牌传给前端,让前端存在cookie中,也可以存储在其他的存储空间(比如:localStorage)当中,之后每一次请求都携带cookie进行访问
如果是在同一次会话的多次请求之间,共享数据,我们就可以将共享的数据存储在令牌当中

优缺点
-
优点:
-
支持PC端、移动端
-
解决集群环境下的认证问题
-
减轻服务器的存储压力(无需在服务器端存储)
-
-
缺点:需要自己实现(包括令牌的生成、令牌的传递、令牌的校验)
JWT令牌
JWT的组成: (JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)
-
第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
-
第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
-
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
JWT令牌的生成和校验
生成
Jwt在生成时首先导入jwt依赖才能使用Jwts中的方法:
<!-- JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Jwts中的builder()用来构建JWT
signWith()用来设计签名算法(这里用的官网HS256),密钥
addClaims()参数需要一个Map,作用是添加自定义信息也就是设置payload
setExpiation()设置过期时间
compact()是根据前面signWith方法的传来的签名算法和密钥来进行构建healder和Signature(签名)

校验
需要用到Jwts.parser()方法进行构建,在解析时只需要对应的密钥和jwt就行
getBody是获取payload进行解析
Claims本质是Map

JWT令牌在登录时下发
通过AI生成JwtUtils工具类
package org.example.pojo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
// 密钥 - 注意:生产环境中应从配置文件读取
private static final String SIGNING_KEY = "eHVodWFu";
// 过期时间:1小时(与测试类一致)
private static final long EXPIRATION_TIME = 3600 * 1000;
/**
* 生成JWT
* @param claims JWT中要包含的声明信息
* @return 生成的JWT字符串
*/
public static String generateJwt(Map<String, Object> claims) {
return Jwts.builder()
.signWith(SignatureAlgorithm.HS256, SIGNING_KEY)
.addClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.compact();
}
/**
* 解析JWT
* @param token JWT字符串
* @return 解析出的声明信息
*/
public static Claims parseJwt(String token) {
return Jwts.parser()
.setSigningKey(SIGNING_KEY)
.parseClaimsJws(token)
.getBody();
}
}
在登录接口的实现类中调用类中的方法,把null值改成jwt

接下来登录后,每次发送请求就会携带请求头token

过滤器filter
JavaWeb三大组件(Servlet、Filter、Listener)之一,使用过滤器后,客户端的所有请求都要经过过滤器,过滤器如果放行才能访问后续的接口
一般用于登录校验,统一编码处理,敏感字符处理

Filter入门
步骤如下

创建Filter文件夹
![]()
从上到下依次时初始化(启动时运行),令牌有效时拦截请求(客户端发送请求时运行),关闭过滤器(在服务器关闭时运行)
@WebFilter注解表示拦截请求(/*)拦截所有请求
在实现时要实现servlet相关的Filter

启动类中添加注解@ServletComponentScan

登录校验过滤器实现
思路如下

在获取请求头时的一些方法的区分

在类中一定要用@WebFilter注解,不然不会经过该过滤器
获取请求数据要强转为HttpServletRequest,才能使用这些request方法,response同理
主要是断点调试来看执行流程(Debug模式)

Filter详解
在过滤器放行后,还可以编写放行后的代码

过滤器链上过滤器的执行顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。 比如:
-
AbcFilter
-
DemoFilter
这两个过滤器来说,AbcFilter 会先执行,DemoFilter会后执行。
过滤器可以有多个执行过程如下

还可以配置拦截路径:
2中还可以配置/login这样的单独的路径

拦截器Interceptor
与Filter类似

使用步骤:
定义拦截器
注册配置拦截器
第一个方法是在资源方法运行后才运行
第二个方法是在视图选然后运行

登录令牌校验
准备两个类一个注册器一个拦截器

定义拦截器
实现HandlerInterceptor接口
这里业务逻辑一致
部分不同:
request,response本身就是HttpServlet类型

注册配置拦截器
@Configuration注解表明这是配置类
实现WebMvcConfigurer接口的注册拦截器方法

Interceptor详解
拦截器的拦截路径
在注册器中我们可以调用一个方法来管理不拦截的路径,能代替我们签名的业务中判断是否为login接口



若存在Filter和Interceptor执行流程如下
其中绿色部分:
被 Filter 放行的请求会到达 Dispatcher Servlet (前端控制器)。这是 Spring MVC 的核心组件,负责接收所有进入 Web 应用的 HTTP 请求,然后根据配置(比如 @RequestMapping注解)将请求分发给相应的 Controller (控制器) 进行业务逻辑处理。

以上就是拦截器的执行流程。通过执行流程分析,大家应该已经清楚了过滤器和拦截器之间的区别,其实它们之间的区别主要是两点:
-
接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
-
拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。
总结
知道了Cookie,Token,Session的概念,作用
令牌的创建和使用
拦截器和过滤器的创建和使用
登录校验时的执行流程
更多推荐

所有评论(0)