登录功能实现

        post请求,json请求参数因要传入Emp对象中的两个属性,所以要用@RequestBody注解

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

Controller层

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

Service层

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

Mapper层

会话技术

会话指的就是浏览器与服务器之间的一次连接,称为一次会话,在一次会话当中,是可以包含多次请求和响应的。

可以多个浏览器和同一个服务器发起会话

会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据

目的就是完成在同一个会话中,多个请求之间进行共享数据。

  1. Cookie(客户端会话跟踪技术):数据存储在客户端浏览器当中

  2. Session(服务端会话跟踪技术):数据存储在储在服务端

  3. 令牌技术

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的概念,作用

令牌的创建和使用

拦截器和过滤器的创建和使用

登录校验时的执行流程

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐