拦截器Interceptor

什么是拦截器?

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

拦截器的作用:

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

拦截器与过滤器主要作用:

是用来对请求进行拦截的,如果是登录请求则直接放行,如果是其他请求,进行拦截,判断该请求携带的token是否没过期,是否正确,如果没过期并且是正确的,则进行放行该请求

快速入门:

步骤:

1.定义拦截器

自定义拦截器:实现HandlerIntercrptor接口,并重写所有方法

//自定义拦截器
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    //目标资源方法执行前执行。 返回true:放行    返回false:不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle .... ");return true; //true表示放行
}

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

//视图渲染完毕后执行,最后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("afterCompletion .... ");
}

}

注意:

​ preHandle方法:目标资源方法执行前执行。 返回true:放行 返回false:不放行

​ postHandle方法:目标资源方法执行后执行

​ afterCompletion方法:视图渲染完毕后执行,最后执行

2.注册配置拦截器

注册配置拦截器**:实现WebMvcConfigurer接口,并重写addInterceptors方法

@Configuration  
public class WebConfig implements WebMvcConfigurer {

    //自定义的拦截器对象
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //注册自定义拦截器对象
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
    }
}

重新启动SpringBoot服务,打开postman测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-037p5xZs-1690099901757)(D:\develop\黑马培训\images\1690093492302.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7XvGUme-1690099901759)(D:\develop\黑马培训\images\1690097395272.png)]

接下来我们再来做一个测试:将拦截器中返回值改为false

使用postman,再次点击send发送请求后,没有响应数据,说明请求被拦截了没有放行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kqotu0fe-1690099901761)(D:\develop\黑马培训\images\1690093533093.png)]

拦截路径

在拦截器中除了可以设置/**拦截所有资源外,还有一些常见拦截路径设置:

拦截路径含义举例
/*一级路径能匹配/depts,/emps,/login,不能匹配 /depts/1
/**任意级路径能匹配/depts,/depts/1,/depts/1/2
/depts/*/depts下的一级路径能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/**/depts下的任意级路径能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1

执行流程

介绍完拦截路径的配置之后,接下来我们再来介绍拦截器的执行流程。通过执行流程,大家就能够清晰的知道过滤器与拦截器的执行时机。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V18zMHAN-1690099901761)(D:/develop/%E9%BB%91%E9%A9%AC%E5%9F%B9%E8%AE%AD/%E5%B0%B1%E4%B8%9A%E7%8F%ADweb%E5%BC%80%E5%8F%91/03-Web%E5%BC%80%E5%8F%91-%E9%82%93%E6%98%8C%E6%B6%9B-KC05558JX/day12-SpringBootWeb%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81/%E8%AE%B2%E4%B9%89/assets/image-20230107112136151.png)]

  • 当我们打开浏览器来访问部署在web服务器当中的web应用时,此时我们所定义的过滤器会拦截到这次请求。拦截到这次请求之后,它会先执行放行前的逻辑,然后再执行放行操作。而由于我们当前是基于springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我们所定义的controller当中的接口方法。
  • Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环境中提供了一个非常核心的Servlet:DispatcherServlet(前端控制器),所有请求都会先进行到DispatcherServlet,再将请求转给Controller。
  • 当我们定义了拦截器后,会在执行Controller的方法之前,请求被拦截器拦截住。执行preHandle()方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller中的方法也不会执行)。
  • 在controller当中的方法执行完毕之后,再回过来执行postHandle()这个方法以及afterCompletion() 方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放行后的这一部分逻辑的逻辑。执行完毕之后,最终给浏览器响应数据。

登录校验(是否登录成功)案例:(1.自定义登录拦截器 2.注册配置拦截器 3.编写登录代码)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TtHNfLfS-1690099901762)(D:\develop\黑马培训\images\1690099873105.png)]

拦截器实现登录校验步骤:

  1. 获取请求路径
  2. 判断是否是登录请求
  3. 如果是登录请求则直接放行
  4. 如果不是则获取登录成功后生成的JWT令牌(token)
  5. 判断令牌是否存在且有效,如果不存在其无效则返回未登录结果
  6. 如果令牌存在且有效,则进行解析token,解析失败返回未登录结果
  7. 解析成功,放行

代码实现:

a. 实现登录基本功能

package com.itheima.controller;

import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import com.itheima.until.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class LoginController {
    @Autowired
    private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp) {
    Emp emp1 = empService.login(emp);
    if (emp1 != null) {//登录成功
        //自定义信息
        Map<String , Object> claims = new HashMap<>();
        claims.put("id", emp1.getId());
        claims.put("username",emp1.getUsername());
        claims.put("name",emp1.getName());
        //敏感性信息不能放到token中
        //claims.put("password",emp1.getPassword())
        String token = JwtUtils.generateJwt(claims);
        return Result.success(token);
    }
    //登录失败
    return Result.error("账户或密码错误");
}
}

b. 生成token,并返回

package com.itheima.until;

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 String signKey = "itheima";//签名密钥
    private static Long expire = 43200000L; //有效时间

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     * SignatureAlgorithm.HS256 算法
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)//自定义信息(有效载荷)
                .signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
                .setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间
                .compact();
        return jwt;
    }


    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)//指定签名密钥
                .parseClaimsJws(jwt)//指定令牌Token
                .getBody();
        return claims;
    }
}

c. 定义一个配置类,配置拦截器

package com.itheima.config;

import com.itheima.interceptor.LoginInterceptor;
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 LoginInterceptor loginInterceptor;

    /**
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册自定义拦截器对象
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
        //设置拦截器拦截的请求路径( /** 表示拦截所有请求)
        //excludePathPatterns("不拦截路径")`方法,指定哪些资源不需要拦截。
    }
}

d. 定义拦截器,并进行解析token,返回解析成功,则放行该请求

package com.itheima.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.until.JwtUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

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

@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //获取令牌token
        String token = request.getHeader("Token");
        //判断token是否为空
        if (!StringUtils.hasLength(token)) {//令牌为空
            //令牌不存在,未登录
            Result result = Result.error("NOT_LOGIN");
            //把对象转换成json字符串
            String s = JSONObject.toJSONString(result);
            //把内容响应给浏览器
            response.getWriter().write(s);
            return false;//不用执行下面代码
        }
        //token不为空 ,校验token
        try {
            JwtUtils.parseJWT(token);
            //解析成功
        } catch (Exception e) {
            //解析失败,未登录,或登录已过期
            Result result = Result.error("NOT_LOGIN");
            //把对象转换成字符串
            String s = JSONObject.toJSONString(request);
            //把内容响应给浏览器
            response.getWriter().write(s);
            return false;//不放行
        }
        //校验成功且token不为空,放行,可以访问具体内容(如:访问员工列表,校验成功则可以看见所有员工列表)
        return true;
    }
}

e. 解析token的代码

package com.itheima.until;

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 String signKey = "itheima";//签名密钥
    private static Long expire = 43200000L; //有效时间

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     * SignatureAlgorithm.HS256 算法
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)//自定义信息(有效载荷)
                .signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
                .setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间
                .compact();
        return jwt;
    }


    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)//指定签名密钥
                .parseClaimsJws(jwt)//指定令牌Token
                .getBody();
        return claims;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值