实战SpringBoot集成JWT实现token验证

 更新时间:2022年1月1日 14:10  点击:303 作者:炒焖煎糖板栗

JWT可以理解为一个加密的字符串,里面由三部分组成:头部(Header)、负载(Payload)、签名(signature)

由base64加密后的header和payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了JWT字符串

往期介绍了JWT相关概念以及基本操作,接下来介绍如何在SpringBoot中整合JWT实现登陆注册

环境搭建

1、新建一个SpringBoot项目Jwt-Demo,引入项目后面需要用到的jar包

  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--引入mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--引入mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <!--引入druid数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.1</version>
        </dependency>
        <!--引入lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--引入jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
    </dependencies>

2、数据库结构

有一个JWT库,里面还有一个User表

3、配置文件application.properties

server.port=8989

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/JWT?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=12345678

#mybatis扫描的包
mybatis.type-aliases-package=com.ylc
#mapper文件路径
mybatis.mapper-locations=classpath:/**/*.xml

#开启sql打印日志  logging.level后面是mybatis对应的方法接口所在的包
logging.level.com.ylc.jwtdemo.dao=debug

4、Entity包下新建一个User类

import lombok.Data;

@Data
public class User {
    private  String username;
    private  String password;
    private  int id;
}

5、Dao包下新建一个UserDao

@Mapper
public interface UserDao {

    User login(User user);
}

6、Service包下新建一个USerService

public interface UserService {
    User login(User user);//登录接口
}

7、UseService的实现类UserServiceImp

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

@Service
public class UserServiceImpI implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public User login(User user) {
        User userdb=userDao.login(user);
        if(userdb!=null)
        {
            Map<String,String> map=new HashMap<>();
            map.put("name",userdb.getUsername());
            return userdb;
        }
        throw  new RuntimeException("登录失败");
    }
}

8、controller包下新建一个UserController

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/login")
    public Map<String,Object> login(User user)
    {
        log.info("用户名:"+user.getUsername());
        log.info("密码:"+user.getPassword());
        Map<String,Object> map=new HashMap<>();
        try {
            userService.login(user);
            map.put("msg","登录成功");
            map.put("code","200");
        }
        catch (Exception ex)
        {
            map.put("msg","登录失败");
        }

        return map;
    }
}

9、在resource文件夹下新建一个Usermapper文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace 指的是要配置的全限定类名-->
<mapper namespace="com.ylc.jwtdemo.dao.UserDao">
    <select id="login" parameterType="com.ylc.jwtdemo.entity.User" resultType="com.ylc.jwtdemo.entity.User">
        select *from user where username=#{username} and password=#{password}
    </select>
</mapper>

10、JWT工具类JwtUtils

/**
 * JWT工具类
 * @author yanglingcong
 * @date 2021/12/31 11:24 AM
 */
public class JwtUtils {
    //鉴权 相当于私钥保存在服务器上
    private  static  final String  secret="##@$%@#S#WS";


    /**
     * 生成token
     * @author yanglingcong
     * @date 2021/12/31 11:23 AM
     * @param map
     * @return String
     */
    public static  String getToken(Map<String,String> map)
    {
        Calendar instance=Calendar.getInstance();
        //默认七天过期
        instance.add(Calendar.DATE,7);
        //创建JWT
        JWTCreator.Builder builder = JWT.create();

        //payload
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        //指定令牌过期时间
        builder.withExpiresAt(instance.getTime());

        String token=builder.sign(Algorithm.HMAC256(secret));
        return token;
    }

    /**
     * 验证token
     * @author yanglingcong
     * @date 2021/12/31 11:26 AM
     * @param token
     */
    public  static  DecodedJWT  verify(String token) {
        return  JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
    }

}

整个项目概览

测试验证是否能够连通数据库

访问:localhost:8989/user/login?username=ylc&password=123456

引入JWT

@Slf4j
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/login")
    public Map<String,Object> login(User user)
    {
        log.info("用户名:"+user.getUsername());
        log.info("密码:"+user.getPassword());
        Map<String,Object> map=new HashMap<>();
        try {
            userService.login(user);
            map.put("msg","登录成功");
            map.put("code","200");

            Map<String,String> payload=new HashMap<>();
            payload.put("name",user.getUsername());
            String token= JwtUtils.getToken(payload);
            map.put("token",token);
        }
        catch (Exception ex)
        {
            map.put("msg","登录失败");
        }

        return map;
    }

    @PostMapping("/test/verity")
    public  Map<String,String> verityToken(String token)
    {
        Map<String, String> map=new HashMap<>();
        log.info("token为"+token);
        try {
            DecodedJWT verify = JwtUtils.verify(token);
            map.put("msg","验证成功");
            map.put("state","true");
        }
        catch (Exception exception)
        {
            map.put("msg","验证失败");
            exception.printStackTrace();
        }
        return map;
    }
}

登录操作

访问:http://localhost:8989/user/login?username=ylc&password=123456

验证操作

访问:http://localhost:8989/test/verity

但是我们这样写在实际项目中是不合理的,把token生成的代码放在了Controller中,业务逻辑是不能放在Controller层中的。假如很多接口都需要token来进行验证保护,那每一个接口都需要添加这样一段代码,造成代码冗余。

程序优化

如果是web项目使用拦截器进行优化,如果是springcloud项目在网关层进行拦截,下面演示如何使用拦截器拦截

最好还把JWT生成token放在http请求头,这样就不需要把token当成参数传递了

新建一个拦截器JwtInterceptor

/**
 * JWT拦截器
 * @author yanglingcong
 * @date 2021/12/31 12:39 PM
 */
public class JwtInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HashMap<String, String> map=new HashMap<>();
        //从http请求头获取token
        String token = request.getHeader("token");
        try {
            //如果验证成功放行请求
            DecodedJWT verify = JwtUtils.verify(token);
            return true;
        }
        catch (Exception exception)
        {
            map.put("msg","验证失败:"+exception);
        }
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json:charset=UTF=8");
        response.getWriter().println(json);
        return false;
    }
}

然后把拦截器注册到过滤器中,新建一个过滤器InterceptConfig

/**
 * @author yanglingcong
 */
@Configuration
public class InterceptConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器
        registry.addInterceptor(new JwtInterceptor())
                //拦截的路径 需要进行token验证的路径
                .addPathPatterns("/test/verity")
                //放行的路径
                .excludePathPatterns("/user/login");
    }
}

登录是不需要拦截的,其他请求如果有需要验证token就放入拦截器的路径

测试验证

在http请求头中放入token,会被拦截器拦截验证token的有效性

总结

这就是SpringBoot整合JWT实际项目一个大概的流程,但是细节方面secret(私钥)肯定每个用户都是不一样的,这里给写死了,而且私钥得保存在一个安全的地方。包括payload部分不能存放敏感的密码信息等等,还可以进行优化。

项目代码:https://gitee.com/yanglingcong/jwt-demo

到此这篇关于实战SpringBoot集成JWT实现token验证的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

原文出处:https://www.cnblogs.com/cg-ww/p/15752750.html

[!--infotagslink--]

相关文章

  • 解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题

    这篇文章主要介绍了解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-28
  • SpringBoot实现excel文件生成和下载

    这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
  • 详解springBoot启动时找不到或无法加载主类解决办法

    这篇文章主要介绍了详解springBoot启动时找不到或无法加载主类解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • SpringBoot集成Redis实现消息队列的方法

    这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
  • 解决Springboot get请求是参数过长的情况

    这篇文章主要介绍了解决Springboot get请求是参数过长的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-17
  • Spring Boot项目@RestController使用重定向redirect方式

    这篇文章主要介绍了Spring Boot项目@RestController使用重定向redirect方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-02
  • Springboot+TCP监听服务器搭建过程图解

    这篇文章主要介绍了Springboot+TCP监听服务器搭建过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-10-28
  • springBoot 项目排除数据库启动方式

    这篇文章主要介绍了springBoot 项目排除数据库启动方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-10
  • Python3使用Selenium获取session和token方法详解

    这篇文章主要介绍了Python3使用Selenium获取session和token方法详解,需要的朋友可以参考下...2021-02-17
  • 详解SpringBoot之访问静态资源(webapp...)

    这篇文章主要介绍了详解SpringBoot之访问静态资源(webapp...),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-14
  • SpringBoot接口接收json参数解析

    这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
  • vue项目中js-cookie的使用存储token操作

    这篇文章主要介绍了vue项目中js-cookie的使用存储token操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-14
  • springboot中使用@Transactional注解事物不生效的坑

    这篇文章主要介绍了springboot中使用@Transactional注解事物不生效的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-26
  • springboot多模块包扫描问题的解决方法

    这篇文章主要介绍了springboot多模块包扫描问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用详解

    这篇文章主要介绍了Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-18
  • Springboot实现多线程注入bean的工具类操作

    这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
  • Springboot+MDC+traceId日志中打印唯一traceId

    本文主要介绍了Springboot+MDC+traceId日志中打印唯一traceId,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-17
  • SpringBoot部署到Linux读取resources下的文件及遇到的坑

    本文主要给大家介绍SpringBoot部署到Linux读取resources下的文件,在平时业务开发过程中,很多朋友在获取到文件内容乱码或者文件读取不到的问题,今天给大家分享小编遇到的坑及处理方案,感兴趣的朋友跟随小编一起看看吧...2021-06-21
  • 关于springboot中nacos动态路由的配置

    这篇文章主要介绍了springboot中nacos动态路由的配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
  • SpringBoot高版本修改为低版本时测试类报错的解决方案

    这篇文章主要介绍了SpringBoot高版本修改为低版本时测试类报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-18