超详细的前后端实战项目(Spring系列加上vue3)前后端篇(四)(一步步实现+源码)

超详细的前后端实战项目(Spring系列加上vue3)前后端篇(四)(一步步实现+源码)

码农世界 2024-05-27 前端 112 次浏览 0个评论

兄弟们,继昨天的代码之后,继续完成最后的用户模块开发,

昨天已经完成了关于用户的信息编辑页面这些,今天再完善一下,

从后端这边开始吧,做一个拦截器,对用户做身份校验,

拦截器

这边先解释一下吧:

那就再用面试题来细说:

拦截器相关面试题:过滤器与拦截器有什么区别?

答:

  • 一:运行的顺序不同
    • 过滤器是servlet容器接收到请求之后,在servlet被调用之前运行的
      • 拦截器则是在servlet被调用之后,但是在响应被发送到客户端之前运行的
        • 二:配置方式不同
          • 过滤器是在web.xml配置
            • 拦截器是在spring的配置文件中配置,或者基于注解进行配置
          • 三:依赖关系
            • Filter依赖于Servlet容器,而Interceptor不依赖于Servlet容器
          • 四:能力方面
            • Filter在过滤器中只能对request和response进行操作
              • Interceptor可以对request,response,handler,modelAndView,exception,相当于Interceptor多了对SpringMvc生态下组件的一个操作能力
            • 接口规范不同
              • 过滤器需要实现Filter接口,拦截器需要实现HandlerInterceptor接口
            • 拦截范围不同
              • 过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源

              OK,来做一个拦截器:

              创建一个interceptors包,并创建LoginInterceptor类

              拦截器的类要继承

              HandlerInterceptor

              注:

              首先,创建拦截器类时,需要实现HandlerInterceptor接口。这个接口定义了三个方法:preHandle()、postHandle()和afterCompletion()。preHandle()方法是在控制器方法执行之前调用的,它返回一个布尔值,表示是否继续执行后续操作;postHandle()方法是在控制器方法执行之后,视图解析之前调用的,可以用来对响应进行进一步的处理;afterCompletion()方法是在整个请求处理完成之后调用的,通常用于资源的清理工作。

              其次,除了实现HandlerInterceptor接口,还可以选择继承HandlerInterceptorAdapter类来简化拦截器的实现。HandlerInterceptorAdapter提供了HandlerInterceptor接口的空实现,使得开发者只需要重写自己关心的方法即可。

              最后,为了让拦截器生效,还需要在Spring配置文件中进行相应的配置。这通常涉及到定义一个配置类,实现WebMvcConfigurer接口,并重写addInterceptors()方法。在这个方法中,可以使用addPathPatterns()来指定拦截路径,使用excludePathPatterns()来指定排除的路径。

              在实际开发中,根据需求选择合适的拦截器类型是非常重要的。例如,如果需要在Controller层进行权限验证,那么使用HandlerInterceptor接口是合适的;如果只是对请求进行简单的预处理和后处理,那么可以考虑使用WebRequestInterceptor接口。选择合适的拦截器类型可以确保代码的整洁性和可维护性。

              下面给出代码

              import org.example.cetidenet.utils.JwtUtil;
              import org.example.cetidenet.utils.ThreadLocalUtil;
              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;
              import java.util.Map;
              @Component
              public class LoginInterceptor implements HandlerInterceptor {
                  @Override
                  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                          return false;
              }
                  @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);
                  }
              }

              这里继承HandlerInterceptor之后按快捷键Ctrl+o,然后继承三种方法就行了,这里常用的是第一种preHandle,在请求到Controller层之前会被preHandle拦截并处理,如果返回不为true,则会被拦截。

              创建了拦截器之后还要注册拦截器。

              这里就在之前创建的config包下进行操作

              在之前加载Swagger资源的类下加上代码:

              @Configuration
              public class WebConfiguration extends WebMvcConfigurationSupport {
                  @Autowired
                  private LoginInterceptor loginInterceptor;
                  @Override
                  public void addInterceptors(InterceptorRegistry registry) {
                      //登录接口和注册接口不拦截
                      registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login","/user/register","/doc.html#/home");
                  }

              这里有个小坑(之前单独创建了一个类继承的是WebMvcConfigurer,结果拦截器缺没有起作用,如果是这样,就继承WebMvcConfigueationSupport)

              添加了拦截的的路径和放行的路径之后就可以进行检验了

              OK,这里使用拦截器之后,登录接口可以正常使用,

              而使用更新接口则返回401没有权限。那也就成功了。

              JWT令牌:

              那么使用了拦截器之后拦截了除了登录注册之外的其他的接口,那该如何才能让其使用呢,必然需要验证身份,这里可以使用JWT令牌,

              也就是在用户登录完成之后发放JWT令牌,然后反馈给前端,前端携带其到后端,拦截器对其进行校验,如果带有JWT令牌就放行。

              后端引入JWT令牌依赖

                     
                      
                          com.auth0
                          java-jwt
                          4.4.0
                      
              import com.auth0.jwt.JWT;
              import com.auth0.jwt.algorithms.Algorithm;
              import java.util.Date;
              import java.util.Map;
              public class JwtUtil {
                  private static final String KEY = "CeTide";
              	
              	//接收业务数据,生成token并返回
                  public static String genToken(Map claims) {
                      return JWT.create()
                              .withClaim("claims", claims)
                              .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 ))
                              .sign(Algorithm.HMAC256(KEY));
                  }
              	//接收token,验证token,并返回业务数据
                  public static Map parseToken(String token) {
                      return JWT.require(Algorithm.HMAC256(KEY))
                              .build()
                              .verify(token)
                              .getClaim("claims")
                              .asMap();
                  }
              }
              

              然后在登录接口这里添加代码:

                  public Result login(UserLoginDTO userLoginDTO) {
                      //先获取DTO中的账号面膜
                      String username = userLoginDTO.getUserName();
                      String password = userLoginDTO.getPassword();
                      //先查询数据库中是否有这号人物
                      User user = userMapper.findByUsername(username);
                      //判断是否存在
                      if(user == null){
                          return Result.error("该用户不存在");
                      }
                      String salt = password + "ceTide";
                      String pwd = DigestUtils.md5Hex(salt.getBytes()).toUpperCase();
                      //存在,判断密码是否正确
                      if(!user.getPassword().equals(pwd)){
                          return Result.error("用户密码错误");
                      }
              //        boolean isLog = logService.addUserLogin(user);
              //        if(!isLog){
              //            return Result.error("用户登录日志记录失败");
              //        }
                      //登录成功
                      Map claims = new HashMap<>();
                      claims.put("id", user.getId());
                      claims.put("username", user.getUserName());
                      String token = JwtUtil.genToken(claims);
                      //存在且密码正确
                      return Result.success(token);
                  }

              将用户的id和username放入到token中(虽然但是,尽量不要把密码放进去)

              然后在拦截器这里进行校验:

                  @Override
                  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                      //令牌验证
                      System.out.println("开始验证");
                      String token = request.getHeader("Authorization");
                      //验证令牌
                      try{
                          Map claims = JwtUtil.parseToken(token);
                          //放行
                          return true;
                      }catch (Exception e) {
                          //反馈响应码401
                          response.setStatus(401);
                          return false;
                      }
                  }

              这里如果能够解析就代表拥有JWT令牌,那也就可以放行了

              现在回到前端这里:

              先看一下修改后的前端代码

              
              
              

              在登录之后获取到了JWT值

              “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiaWQiOjE1LCJ1c2VybmFtZSI6IueUqOaIt1VTRERBRCJ9LCJleHAiOjE3MTY4MDQ4ODB9._9GDxuux5wmoV5CsZCd0QI3wByESKWGGZCKmDaZVlbc”

              这样一大串字,然后前端将其放置在请求头的Authorization属性中

              举个例子

              export const userGetInfo= () =>{
                  return request.get('/user/getInfo',{
                      headers : {
                          'Authorization' : 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiaWQiOjE1LCJ1c2VybmFtZSI6IueUqOaIt1VTRERBRCJ9LCJleHAiOjE3MTY4MDQ4ODB9._9GDxuux5wmoV5CsZCd0QI3wByESKWGGZCKmDaZVlbc',
                      }
                  });
              }
              

              此时访问页面,就能得到用户名等信息了

              然后就算是成功实现了请求的拦截与用户身份的检验

转载请注明来自码农世界,本文标题:《超详细的前后端实战项目(Spring系列加上vue3)前后端篇(四)(一步步实现+源码)》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,112人围观)参与讨论

还没有评论,来说两句吧...

Top