JavaWeb后端实战(登录认证 & 令牌技术 & 拦截器 & 过滤器)

文章目录

  • 一、登录功能
    • [1.1 Controller](#1.1 Controller)
    • [1.2 Service](#1.2 Service)
    • [1.3 Mapper](#1.3 Mapper)
  • 二、登录校验
    • [2.1 会话技术](#2.1 会话技术)
      • [2.1.1 会话跟踪方案对比](#2.1.1 会话跟踪方案对比)
        • [2.1.1.1 Cookie](#2.1.1.1 Cookie)
        • [2.1.1.2 Session](#2.1.1.2 Session)
        • [2.1.1.3 令牌](#2.1.1.3 令牌)
    • [2.2 JWT令牌](#2.2 JWT令牌)
      • [2.2.1 介绍](#2.2.1 介绍)
      • [2.2.2 JWT令牌生成与解析](#2.2.2 JWT令牌生成与解析)
      • [2.2.3 登录Server层完善](#2.2.3 登录Server层完善)
    • [2.3 过滤器Filter](#2.3 过滤器Filter)
      • [2.3.1 Filter快速入门](#2.3.1 Filter快速入门)
      • [2.3.2 Filter拦截路径](#2.3.2 Filter拦截路径)
      • [2.3.3 Filter过滤器拦截链](#2.3.3 Filter过滤器拦截链)
      • [2.3.4 Filter登录校验](#2.3.4 Filter登录校验)
    • [2.4 拦截器Interceptor](#2.4 拦截器Interceptor)
      • [2.4.1 Interceptor快速入门](#2.4.1 Interceptor快速入门)
      • [2.4.2 Interceptor拦截路径](#2.4.2 Interceptor拦截路径)
      • [2.4.3 Interceptor执行流程](#2.4.3 Interceptor执行流程)
      • [2.4.4 Interceptor登录校验](#2.4.4 Interceptor登录校验)

一、登录功能

1.1 Controller

java 复制代码
    /**
     * 登录功能
     * @param emp
     * @return
     */
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        log.info("用户id:{};用户名:{}", emp.getId(), emp.getName());
        LoginInfo loginInfo = empService.login(emp);
        if (loginInfo != null) {
            return Result.success(loginInfo);
        }
        return Result.error("登录校验失败");
    }

1.2 Service

java 复制代码
    /**
     * 登录功能
     * @param emp
     * @return
     */
    @Override
    public LoginInfo login(Emp emp) {
        Emp empList = empMapper.login(emp);
        return new LoginInfo(empList.getId(), empList.getUsername(), empList.getName(), null);
    }

1.3 Mapper

java 复制代码
    @Select("select * from emp where username = #{username} and password = #{password}")
    Emp login(Emp emp);

存在的问题:未登录的情况下也可以访问其他功能,登录功能相当于失效

二、登录校验

登录标记:用户登录成功后续的每一次请求中,都可以获取到该标记【会话技术】

统一拦截:过滤器Filter、拦截器Interceptor

2.1 会话技术

①会话:用户打开浏览器,访问web服务器的资源,会话建立,知道有一方断开连接,会话结束。再一次会话中可以包含多次请求和响应

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

③会话跟踪方案:客户端会话跟踪技术:Cookie;服务端会话跟踪技术:Session

2.1.1 会话跟踪方案对比

优点:HTTP协议中支持的技术

缺点:

①移动端APP无法使用Cookie

②不安全,用户可以自己禁用Cookie

③Cookie不能跨域

2.1.1.2 Session

优点:存储在服务端、安全

缺点:

①服务器在集群环境下无法直接使用Session

②Cookie的缺点

2.1.1.3 令牌

优点:

①支持PC端、移动端

②解决集群环境下的认证问题

③减轻服务器端存储压力

缺点:需要自己实现

2.2 JWT令牌

2.2.1 介绍

全称:JSON Web Token(https://jwt.io/)

定义了一种简洁的,自包含的格式,用于在通信双方以Json数据格式安全的传输信息。

组成:

①第一部分:Header(头),记录令牌类型、签名算法。例如:{"alg","HS256","type","JWT"}

②第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。例如:{"id":"1","username":"Tom"}

③第三部分:Sifnature(签名),放置Token被篡改、确保安全性。将herder、payload融入,并加入指定密钥,通过指定签名算法而来

Base64:是一种基于64个可以打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式

2.2.2 JWT令牌生成与解析

①添加依赖:

java 复制代码
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
java 复制代码
    /**
     * 生成令牌
     */
    @Test
    public void genJwt() {
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", 10);
        claims.put("username", "Tom");
        String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "chenxd")
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + 12 * 3600 * 1000))
                .compact();
        System.out.println(jwt);
    }
java 复制代码
    /**
     * 令牌校验(解析)
     */
    @Test
    public void testParseJwt() {
        Claims claims = Jwts.parser().setSigningKey("chenxd")
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoiVG9tIiwiZXhwIjoxNzYwNTM3MjkxfQ.CipsO92FFdy-E8cizgo7Db_0aQgvzjEdTEQEPpNAFnE")
                .getBody();
        System.out.println(claims);

    }

注意:JWT校验时使用的签名密钥,必须和生成JWT令牌时使用的密钥匙配套的

2.2.3 登录Server层完善

①添加工具类

java 复制代码
/**
 * @author Tender
 * @date 2025/10/15 10:16
 */

package com.chenxd.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtils {
    
    private JwtUtils(){}
    /**
     * Jwt生成
     * @param claims
     * @param s
     * @return
     */
    public static String jwtBuilder(Map<String,Object> claims,String s){
        String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, s)
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + 12 * 3600 * 1000))
                .compact();
        return jwt;
    }

    /**
     * 解析
     * @param jwt
     * @return
     */
    public static Claims jwtParse(String jwt){
        Claims claims = Jwts.parser().parseClaimsJws(jwt)
                .getBody();

        return claims;
    }
}

②修改Service层

java 复制代码
 /**
     * 登录功能
     *
     * @param emp
     * @return
     */
    @Override
    public LoginInfo login(Emp emp) {
        Emp empList = empMapper.login(emp);
        if (empList != null) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", empList.getId());
            map.put("username", empList.getUsername());
            String token = JwtUtils.jwtBuilder(map, "chenxd");
            return new LoginInfo(empList.getId(), empList.getUsername(), empList.getName(), token);
        }
        return null;
    }

2.3 过滤器Filter

①概念:Filter过滤器,是JavaWeb三大组件(Servlet,Filter、Listener)之一

②过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能

③过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符集处理等

2.3.1 Filter快速入门

①定义Filter:定义一个类,实现Filter接口,并重写其所有方法

②配置Filter:Filter类上加上@WebFilter注解,配置拦截路径,引导类加上@ServletComponentScan注解开启Servlet

组件支持

java 复制代码
/**
 * @author Tender
 * @date 2025/10/15 10:48
 */

package com.chenxd;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
    // 初始化方法,Web服务器启动,创建Filter示例时调用,只调用一次
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    // 拦截到请求时,调用该方法,可以调用多次
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 放行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    // 销毁方法,web服务关闭时调用,只调用一次
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
java 复制代码
@ServletComponentScan
@SpringBootTest
class Day03LimitTestApplicationTests {
}

问题1:放行后访问对应资源,资源访问完成后,还会回到Filter中吗?

问题2:如果回到Filter中,是重新执行还是执行放行后的逻辑呢?

执行放行后的逻辑

2.3.2 Filter拦截路径

Filter可以根据需求,配置不同的拦截资源路径

2.3.3 Filter过滤器拦截链

介绍:一个Web应用中可以配置多个拦截器,这多个拦截器就形成了拦截器链

顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序

2.3.4 Filter登录校验

思考:

所有的请求拦截到了之后,都需要校验令牌吗?
有一个例外,登录请求

拦截到请求后什么情况下才可以放行,执行业务操作?
有令牌,且令牌校验通过(合法):否则都返回未登录错误结果

流程:

①获取请求url

②判断请求url中是否包含login,如果包含说明是的登录操作,放行

③获取请求头中的令牌(token)

④判断令牌是是否存在,如果不存在,响应401

⑤解析token,如果解析失败,响应401

⑥放行

java 复制代码
/**
 * @author Tender
 * @date 2025/10/15 11:18
 */

package com.chenxd.filter;

import com.chenxd.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Slf4j
@WebFilter("/*")
public class LoginCheckFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // ①获取请求url
        String requestURI = request.getRequestURI();
        // ②判断请求url中是否包含login,如果包含说明是的登录操作,放行
        if (requestURI.contains("login")) {
            log.info("登录成功");
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        // ③获取请求头中的令牌(token)
        String token = request.getHeader("token");
        // ④判断令牌是是否存在,如果不存在,响应401
        if (token == null) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            log.error("令牌为空");
            return;
        }
        // ⑤解析token,如果解析失败,响应401
        try {
            JwtUtils.jwtParse(token);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            log.info("令牌解析失败");
            return;
        }
        // ⑥放行
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

2.4 拦截器Interceptor

概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架提供的,主要用来动态拦截控制器方法的执行

作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码

2.4.1 Interceptor快速入门

①定义拦截器,实现HandlerInterceptor

②注册拦截器

java 复制代码
/**
 * @author Tender
 * @date 2025/10/15 11:51
 */

package com.chenxd;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class DemoInterceptor implements HandlerInterceptor {


    // 目标资源方法执行前执行,返回true:放行;返回false:不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    // 目标资源方法执行后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    // 视图渲染完毕后执行,最后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
java 复制代码
/**
 * @author Tender
 * @date 2025/10/15 11:55
 */

package com.chenxd;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private DemoInterceptor demoInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(demoInterceptor)
                .addPathPatterns("/**");
    }
}

2.4.2 Interceptor拦截路径

拦截器可以根据需求配置不同的拦截路径:

2.4.3 Interceptor执行流程

2.4.4 Interceptor登录校验

java 复制代码
/**
 * @author Tender
 * @date 2025/10/15 12:04
 */

package com.chenxd.Interceptor;

import com.chenxd.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // ①获取请求url
        String requestURI = request.getRequestURI();
        // ②判断请求url中是否包含login,如果包含说明是的登录操作,放行
        if (requestURI.contains("login")) {
            log.info("登录成功");
            return true;
        }
        // ③获取请求头中的令牌(token)
        String token = request.getHeader("token");
        // ④判断令牌是是否存在,如果不存在,响应401
        if (token == null) {
            log.info("token不存在");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        // ⑤解析token,如果解析失败,响应401
        try {
            Claims claims = JwtUtils.jwtParse(token);
            log.info("令牌解析内容,{}", claims);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        // ⑥放行
        return true;
    }
}
java 复制代码
/**
 * @author Tender
 * @date 2025/10/15 12:12
 */

package com.chenxd.config;

import com.chenxd.Interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**");
    }
}

Fileter与Interceptor的区别:
①接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口
②拦截范围不同:过滤器Filter会拦截所有资源,而Interceptor只会拦截Spring环境中的资源

相关推荐
超级大只老咪15 小时前
数组相邻元素比较的循环条件(Java竞赛考点)
java
小浣熊熊熊熊熊熊熊丶15 小时前
《Effective Java》第25条:限制源文件为单个顶级类
java·开发语言·effective java
毕设源码-钟学长15 小时前
【开题答辩全过程】以 公交管理系统为例,包含答辩的问题和答案
java·eclipse
啃火龙果的兔子15 小时前
JDK 安装配置
java·开发语言
星哥说事15 小时前
应用程序监控:Java 与 Web 应用的实践
java·开发语言
派大鑫wink15 小时前
【JAVA学习日志】SpringBoot 参数配置:从基础到实战,解锁灵活配置新姿势
java·spring boot·后端
xUxIAOrUIII16 小时前
【Spring Boot】控制器Controller方法
java·spring boot·后端
Dolphin_Home16 小时前
从理论到实战:图结构在仓库关联业务中的落地(小白→中级,附完整代码)
java·spring boot·后端·spring cloud·database·广度优先·图搜索算法
等....16 小时前
Miniconda使用
开发语言·python
zfj32116 小时前
go为什么设计成源码依赖,而不是二进制依赖
开发语言·后端·golang