过滤器拦截器生效方式

1. 过滤器(Filter)
作用
请求预处理:在请求到达控制器之前进行预处理。
响应后处理:在响应返回客户端之前进行后处理。
通用功能:适用于跨多个控制器的通用功能,如日志记录、身份验证、字符编码设置等。
实现方式
实现 javax.servlet.Filter 接口。
使用 @Component 注解:让 Spring 管理该 Bean。
可选:使用 @WebFilter 注解。会自动注册

注册方式
自动注册:
使用 @Component 注解,Spring Boot 会自动将其注册到 Servlet 容器中。
手动注册:
使用 FilterRegistrationBean 手动注册过滤器。

2. 拦截器(Interceptor)
作用
请求预处理:在请求到达控制器之前进行预处理。
响应后处理:在控制器处理请求后,但在视图渲染之前进行后处理。
视图渲染后处理:在视图渲染完成后执行。
特定功能:适用于特定控制器或特定请求路径的功能,如权限检查、日志记录等。
实现方式
实现 org.springframework.web.servlet.HandlerInterceptor 接口。

注册方式
通过 WebMvcConfigurer 接口注册拦截器:

3、JWT令牌生成token的工具类

生成的token,是3段,用.连接。其中:前两段头部和载荷都是通过Base64进行编码的,后最后一段是将前两段连接在一起,然后在通过相应的加密算法(这里HS256)进行加密过后的字符串。

generateToken()方法和parseToken()方法标识为static静态,就可以直接在filter和intercepter用【类名.方法名】的调用jwt工具类生成token
package cn.swu.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;

//利用jwt令牌生成token
@Component
public class JwtUtils {
    // 密钥,用于签名和验证JWT
    private static final String SECRET_KEY = "bHdmdHcKCg==";

    // 过期时间,单位为毫秒,2小时
    private static final long EXPIRATION_TIME = 2 * 60 * 60 * 1000;

    /**
     * 生成JWT令牌

     * @return 生成的JWT令牌
     */
    public static String generateToken(Map<String, Object> claims) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);

        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .addClaims(claims)
                .setExpiration(expiryDate)
                .compact();
    }

    /**
     * 解析JWT令牌
     *
     * @param token JWT令牌
     * @return 解析后的Claims对象
     * @throws RuntimeException 如果令牌无效或过期
     */
    public static Claims parseToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(SECRET_KEY)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            throw new RuntimeException("Invalid or expired JWT token", e);
        }
    }

    /**
     * 从Claims中获取主题
     *
     * @param claims Claims对象
     * @return 主题
     */
    public String getSubjectFromClaims(Claims claims) {
        return claims.getSubject();
    }
}
package cn.swu.filter;

import cn.swu.utils.CurrentHolder;
import cn.swu.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
;

import java.io.IOException;
@Slf4j
//@Component
@WebFilter(urlPatterns = "/*")
public class TokenFilter implements Filter {

        //@Autowired
      // private JwtUtils jwtUtils;//静态方法不用依赖注入初始化

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println(JwtUtils.class);

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //获取请求路径
        String requestURI = request.getRequestURI();
        // 判断请求路径是否是登录请求,如果是登录请求,直接放行
        if (requestURI.startsWith("/login")) {
            filterChain.doFilter(request, response);
            return;
        }
        //获取请求头中的token
        String token = request.getHeader("token");
        //判断token是否为空,如果为空,则提示用户登录,返回401状态码
        if (token == null || token.isEmpty()) {
           // System.out.println(token);
            response.setStatus(401);
            response.getWriter().write("请先登录");
            return;
        }
        //如果token不为空,则解析校验令牌oken,如果解析失败,则提示用户登录,返回401状态码
        //如果校验成功,则放行
        try {
            Claims claims = JwtUtils.parseToken(token);
            //解析token,获取ID,将ID设置给当前线程,方便AOP获取当前线程中的用户登录ID
            Integer id = Integer.valueOf(claims.get("id").toString());
            CurrentHolder.setCurrentId(id);//静态方法可直接用类名来调用
            log.info("当前线程中的用户登录ID为:{}", id);
            filterChain.doFilter(request, response);
            //帮助AOP获得当前用户ID之后,过滤器放行该请求之后其他层处理完该请求,再进行业务操作,删除当前thread的ThreadLocal中的id值
            CurrentHolder.remove();
        } catch (Exception e) {
            response.setStatus(401);
            response.getWriter().write("请先登录");
        }


    }
}
package cn.swu.interceptor;

import cn.swu.utils.CurrentHolder;
import cn.swu.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;


@Component
@Slf4j
public class TokenInterceptor implements HandlerInterceptor {
    @Autowired
    private JwtUtils jwtUtils;



    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("preHandle");
      /*  //获取请求路径
        String requestURI = request.getRequestURI();
        // 判断请求路径是否是登录请求,如果是登录请求,直接放行
        if (requestURI.startsWith("/login")) {
            return true;
        }*/
        //获取请求头中的token
        String token = request.getHeader("token");
        //判断token是否为空,如果为空,则提示用户登录,返回401状态码
        if (token == null || token.length() == 0) {
            response.setStatus(401);
            response.getWriter().write("请先登录");
            return false;
        }
        //如果token不为空,则解析校验令牌oken,如果解析失败,则提示用户登录,返回401状态码
        //如果校验成功,则放行
        try {
            Claims claims = jwtUtils.parseToken(token);
            //解析token,获取ID,将ID设置给当前线程,方便AOP获取当前线程中的用户登录ID
            Integer id = Integer.valueOf(claims.get("id").toString());
            CurrentHolder.setCurrentId(id);//静态方法可直接用类名来调用
            log.info("当前线程中的用户登录ID为:{}", id);
            return true;
        } catch (Exception e) {
            response.setStatus(401);
            response.getWriter().write("请先登录");
            return false;
        }

        // true放行, false拦截



    }
    // 过滤器放行之后再进行业务操作,删除当前thread的ThreadLocal中的id值
    //请求处理完成后再执行该方法
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //帮助AOP获得当前用户ID之后,过滤器放行该请求之后再进行业务操作,删除当前thread的ThreadLocal中的id值
        CurrentHolder.remove();
    }
}

intercepter注册需要额外的配置类

WebConfig
package cn.swu.config;

import cn.swu.interceptor.TestInterceptor;
import cn.swu.interceptor.TokenInterceptor;
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 TestInterceptor testInterceptor;
    @Autowired
    private TokenInterceptor tokenInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {  // 拦截器注册
     registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login","/css/**","/js/**","/img/**","/fonts/**","/plugins/**");

    }
}

区别

过滤器(Filter)
适用于所有请求,包括静态资源。
使用 @Component 注解或 FilterRegistrationBean 注册。
适用于通用功能,如身份验证、日志记录等。
拦截器(Interceptor)
仅适用于 Spring MVC 处理的请求。
使用 WebMvcConfigurer 接口注册。
适用于特定控制器或特定请求路径的功能,如权限检查、日志记录等。

注册FIlter时

@Component注解实现浅层原理

 过滤器生效的原因
TokenFilter 类被标记为 @Component,因此 Spring Boot 会自动将其注册到 Servlet 容器中。具体步骤如下:
类扫描:
Spring Boot 在启动时会扫描带有 @Component 注解的类。
TokenFilter 类被标记为 @Component,因此会被扫描到。
Bean 创建:
Spring 会创建 TokenFilter 的 Bean 实例。
自动注册:
由于 TokenFilter 实现了 javax.servlet.Filter 接口,Spring Boot 会自动将其注册到 Servlet 容器中,使其能够拦截 HTTP 请求。

@WebFilter注解如何被 扫描到

在启动类上标识
@ServletComponentScan // 扫描Servlet注解, 如:@WebFilter

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwftw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值