首页 > 其他分享 >springboot项目登陆校验————基于Jwt令牌和Fliter / Interceptor

springboot项目登陆校验————基于Jwt令牌和Fliter / Interceptor

时间:2024-07-02 11:01:04浏览次数:19  
标签:令牌 拦截器 springboot 登录 jwt Jwt Fliter 过滤器 拦截

springboot项目登陆校验————基于Jwt令牌和Fliter / Interceptor

文章目录

1.概述

  在各式各样的项目中,为了保护用户的隐私,增加项目的安全性,都需要添加一个登录功能,只有当登录通过后,才能访问项目内容。
在登录接口中只需要判断前端传来的用户名和密码是否存在,即如下的一个简单的select查询即可实现,

<select id="selectByUsernameAndPassword" resultType="com.ly.springbootdemotlias.pojo.Emp">
        select *from emp where password=#{password} and username=#{username}
    </select>

  但是思考后可以发现,我们仍然可以跳过登录的接口直接通过访问内容的URL地址直接访问到内容。
所以我们必须对这一行为实现,拦截,当用户要访问内容URL时,希望能够跳转回到登陆页面,即实现了校验及拦截。
实现这一功能的核心就是本文章要介绍到的Jwt令牌,过滤器Fliter和拦截器Interceptor。

2.Jwt令牌

  令牌,其实就是一个用户身份的标识,看似很高大上,很神秘,其实本质就是一个字符串。
如果通过令牌技术来跟踪会话,我们就可以在浏览器发起请求。在请求登录接口的时候,如果登录成功,我就可以生成一个令牌,令牌就是用户的合法身份凭证。接下来我在响应数据的时候,我就可以直接将令牌响应给前端。
  接下来我们在前端程序当中接收到令牌之后,就需要将这个令牌存储起来。这个存储可以存储在 cookie 当中,也可以存储在其他的存储空间(比如:localStorage)当中。
  接下来,在后续的每一次请求当中,都需要将令牌携带到服务端。携带到服务端之后,接下来我们就需要来校验令牌的有效性。如果令牌是有效的,就说明用户已经执行了登录操作,如果令牌是无效的,就说明用户之前并未执行登录操作。
  此时,如果是在同一次会话的多次请求之间,我们想共享数据,我们就可以将共享的数据存储在令牌当中就可以了。
image.png

2.1Jwt令牌的组成

image.png

  • 第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{“alg”:“HS256”,“type”:“JWT”}
  • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{“id”:“1”,“username”:“Tom”}
  • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
     签名的目的就是为了防jwt令牌被篡改,而正是因为jwt令牌最后一个部分数字签名的存在,所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡改了,整个令牌在校验的时候都会失败,所以它是非常安全可靠的。

2.2 Jwt登录认证流程

    1. 在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。
    1. 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。
    1. 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。

2.3代码实现

要想使用JWT令牌,需要先引入Jwt的依赖:

<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

为了方便在多个方法中调用,把生成和解析Jwt的方法封装到一个工具类中

public class JwtUtils {
 private static String signKey = " xxxx ";//自定义签名密钥
 private static Long expire = 43200000L; //有效时间

 /**
  * 生成JWT令牌
  * @param claims JWT第二部分负载 payload 中存储的内容
  * @return
  */
 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;
 }
}

在登陆接口中定义:如果登录成功,则生成Jwt令牌并返回

@RestController
@Slf4j
public class LoginController {
    //依赖业务层对象
    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        //调用业务层:登录功能
        Emp loginEmp = empService.login(emp);

        //判断:登录用户是否存在
        if(loginEmp !=null ){
            //自定义信息
            Map<String , Object> claims = new HashMap<>();
            claims.put("id", loginEmp.getId());
            claims.put("username",loginEmp.getUsername());
            claims.put("name",loginEmp.getName());

            //使用JWT工具类,生成身份令牌
            String token = JwtUtils.generateJwt(claims);
            return Result.success(token);
        }
        return Result.error("用户名或密码错误");
    }
}

 在生成了Jwt令牌后,在后续的请求当中,都会在请求头中携带JWT令牌到服务端,而服务端需要统一拦截所有的请求,从而判断是否携带的有合法的JWT令牌。
 那怎么样来统一拦截到所有的请求校验令牌的有效性呢?有两种解决方案:

    1. Filter过滤器
    1. Interceptor拦截器
      我们首先来学习过滤器Filter。

3.方法1:Fliter过滤器

3.1简介

  • Filter表示过滤器,是 JavaWeb三大组件(Servlet、Filter、Listener)之一。
  • 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
  • 使用了过滤器之后,要想访问web服务器上的资源,必须先经过滤器,过滤器处理完毕之后,才可以访问对应的资源。
  • 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
    image.png

3.1 代码实现

Fliter过滤器的实现分为两个步骤:
第1步,定义过滤器 :1.定义一个类,实现 Filter 接口,并重写其所有方法。

  • init方法:过滤器的初始化方法。在web服务器启动的时候会自动的创建Filter过滤器对象,在创建过滤器对象的时候会自动调用init初始化方法,这个方法只会被调用一次。

  • doFilter方法:这个方法是在每一次拦截到请求之后都会被调用,所以这个方法是会被调用多次的,每拦截到一次请求就会调用一次doFilter()方法。

  • destroy方法: 是销毁的方法。当我们关闭服务器的时候,它会自动的调用销毁方法destroy,而这个销毁方法也只会被调用一次。

第2步,配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径(这里使用“/”表示拦截所有),引导类上加 @ServletComponentScan 开启Servlet组件支持。

拦截路径urlPatterns值含义
拦截具体路径/login只有访问 /login 路径时,才会被拦截
目录拦截/emps/*访问/emps下的所有资源,都会被拦截
拦截所有/*访问所有资源,都会被拦截

代码实现如下:

@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements 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 {
         //1.获取请求的URL
        HttpServletRequest req =(HttpServletRequest)servletRequest;
        HttpServletResponse res = (HttpServletResponse) servletResponse;
        String url = req.getRequestURL().toString();
        //2.若是登录操作直接放行
        if(url.contains("login")){
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }
        //3.获取请求中的令牌token
        String jwt = req.getHeader("token");
        //4.判断是否存在,若不存在,返回错误结果
        if(!StringUtils.hasLength(jwt)){
            Result error = Result.error("NOT_LOGIN");
            //手动转换对象----使用fastJSON
            String notLogin = JSONObject.toJSONString(error);
            //响应未登录结果给浏览器
            res.getWriter().write(notLogin);
            return;
        }
            //5.解析token,如果解析失败,返回错误结果(未登录)
        try {
            JwtUtils.parseJwt(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            Result error = Result.error("NOT_LOGIN");
            //手动转换对象----使用fastJSON
            String notLogin = JSONObject.toJSONString(error);
            //响应未登录结果给浏览器
            res.getWriter().write(notLogin);
            return;
        }
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

实现该代码的具体流程图:
image.png

4.方法2:Interceptor拦截器

4.1 简介

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

拦截器的作用:

  • 拦截请求,在指定方法调用前后,根据业务需要执行预先设定的代码。
  • 在拦截器当中,我们通常也是做一些通用性的操作,比如:我们可以通过拦截器来拦截前端发起的请求,将登录校验的逻辑全部编写在拦截器当中。在校验的过程当中,如发现用户登录了(携带JWT令牌且是合法令牌),就可以直接放行,去访问spring当中的资源。如果校验时发现并没有登录或是非法令牌,就可以直接给前端响应未登录的错误信息。

4.2 代码实现

Interceptor拦截器的实现也分为两个步骤:
第1步,定义拦截器:1.定义一个类,实现 HandlerInterceptor,并重写其方法。
因为拦截器是Spring框架中提供的,所以加上@Component注解即可。

preHandle方法:目标资源方法执行前执行。 返回true:放行 返回false:不放行(我们实现拦截的逻辑就是在这个方法中)

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

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

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override//资源方法运行前运行,返回值为true:放行,返回值为false:拦截
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          //1.获取请求的URL
        String url = request.getRequestURL().toString();
        //2.若是登录操作直接放行
        if(url.contains("login")){
            return true;
        }
           //3.获取请求中的令牌token
        String jwt = request.getHeader("token");
         //4.判断是否存在,若不存在,返回错误结果
        if(!StringUtils.hasLength(jwt)){
            Result error = Result.error("NOT_LOGIN");
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return false;
        }
         //5.解析token,如果解析失败,返回错误结果(未登录)
        try {
            JwtUtils.parseJwt(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            Result error = Result.error("NOT_LOGIN");
            //手动转换对象----使用fastJSON
            String notLogin = JSONObject.toJSONString(error);
           //响应未登录结果给浏览器
            response.getWriter().write(notLogin);
            return false;
        }
         //6.放行
        return true;
    }

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

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

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

@Configuration
public class WebConfig implements WebMvcConfigurer {
    //自定义的拦截器对象(上面代码实现的拦截器类)
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册自定义拦截器对象
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
                                       //还可以接excludePathPatterns("/login")方法设置不拦截的请求路径
    }
}

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

拦截路径含义举例
/*一级路径能匹配/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

5.总结

 本文介绍了如何利用Jwt令牌和Fliter过滤器/Interceptor拦截器来实现项目的登录校验功能,过滤器和拦截器的选择需要根据项目实际情况选择,希望通过本文大家能够实现登录校验功能,能对过滤器和拦截器有一个初步了解。

标签:令牌,拦截器,springboot,登录,jwt,Jwt,Fliter,过滤器,拦截
From: https://blog.csdn.net/ASL2915/article/details/140098199

相关文章

  • springboot视频网站系统的设计与实现
    文章目录目录文章目录论文目录项目介绍开发环境系统实现论文参考论文目录1绪论1.1 研究背景1.2目的和意义1.3论文结构安排2 相关技术2.1 SpringBoot框架简介2.2B/S架构介绍2.3MySQL数据库介绍2.4JAVA语言介绍3系统分析3.1系统可行性......
  • 【Springboot】基于AOP实现操作日志记录
    基于AOP实现操作日志记录文章目录基于AOP实现操作日志记录前言一、AOP1.介绍2.AOP核心概念二、基于AOP实现操作日志记录1.准备工作2.创建自定义注解和切面类3.实现日志记录总结前言 在springboot项目中,往往需要在用户完成某些操作(例如:增,删,改)时,能够将相关操作信......
  • springboot健身房管理系统-计算机毕业设计源码031807
    摘 要大数据时代下,数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求,利用互联网服务于其他行业,促进生产,已经是成为一种势不可挡的趋势。在健身房管理的要求下,开发一款整体式结构的健身房管理系统,将复杂的系统进行拆分,能够实现对需求的变化快速响应、系统稳定......
  • 毕业设计-基于Springboot+Vue的班级综合测评管理系统的设计与实现(源码+LW+包运行)
    基于SpringBoot+Vue的班级综合测评管理系统开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示视频:链接:https://pan.baidu.com/s/1N_GWua74rAi1Qtkj1VpmHQ?pwd=zmut随着互联网技术的高速发展,人们生活的各方面都受到互联......
  • 毕业设计-基于Springboot+Vue的冬奥会科普系统的设计与实现(源码+LW+包运行)
    基于SpringBoot+Vue的冬奥会科普系统开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示视频:链接:https://pan.baidu.com/s/1YFTiNrYkLJAyvU40nmzbSg?pwd=x44b任何平台都要遵循平台设计的基本流程,本平台也不例外,同样需要......
  • 毕业设计-基于Springboot+Vue的校友社交系统的设计与实现(源码+LW+包运行)
    源码获取:https://download.csdn.net/download/u011832806/89460925基于SpringBoot+Vue的校友社交系统开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示视频:链接:https://pan.baidu.com/s/1gbqldVNoi7Shkp9jlM-fzg?pwd=tk......
  • 毕业设计-基于Springboot+Vue的毕业生信息招聘平台的设计与实现(源码+LW+包运行)
    源码获取:https://download.csdn.net/download/u011832806/89431634基于SpringBoot+Vue的毕业生信息招聘平台开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示视频:链接:https://pan.baidu.com/s/1-X-CEV8YNsWo7e-pA8pv7g?......
  • 基于java+springboot+vue实现的家政服务平台(文末源码+Lw)299
    摘 要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本家政服务平台就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达......
  • 基于java+springboot+vue实现的旅游管理系统(文末源码+Lw)227
    摘 要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本旅游管理系统就是在这样的大环境下诞生,其可以帮助使用者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达......
  • springboot3(cloud 2022.0.0)整合seata1.7.1
    一、第一步下载对应版本的seata服务  二、修改conf下的application.yml配置注意:主要是连接nacos的一些配置:注册中心和服务发现的配置1#Copyright1999-2019Seata.ioGroup.2#3#LicensedundertheApacheLicense,Version2.0(the"License");4#you......