经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
实战SpringBoot集成JWT实现token验证
来源:jb51  时间:2021/12/31 20:23:17  对本文有异议

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

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

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

环境搭建

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

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-test</artifactId>
  9. <scope>test</scope>
  10. </dependency>
  11. <!--引入mybatis-->
  12. <dependency>
  13. <groupId>org.mybatis.spring.boot</groupId>
  14. <artifactId>mybatis-spring-boot-starter</artifactId>
  15. <version>2.1.3</version>
  16. </dependency>
  17. <!--引入mysql-->
  18. <dependency>
  19. <groupId>mysql</groupId>
  20. <artifactId>mysql-connector-java</artifactId>
  21. <version>8.0.25</version>
  22. </dependency>
  23. <!--引入druid数据库连接池-->
  24. <dependency>
  25. <groupId>com.alibaba</groupId>
  26. <artifactId>druid</artifactId>
  27. <version>1.2.1</version>
  28. </dependency>
  29. <!--引入lombok-->
  30. <dependency>
  31. <groupId>org.projectlombok</groupId>
  32. <artifactId>lombok</artifactId>
  33. <version>1.18.12</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.mybatis.spring.boot</groupId>
  37. <artifactId>mybatis-spring-boot-starter-test</artifactId>
  38. <version>2.1.3</version>
  39. </dependency>
  40. <!--引入jwt-->
  41. <dependency>
  42. <groupId>com.auth0</groupId>
  43. <artifactId>java-jwt</artifactId>
  44. <version>3.4.0</version>
  45. </dependency>
  46. </dependencies>

2、数据库结构

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

3、配置文件application.properties

  1. server.port=8989
  2.  
  3. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  4. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  5. spring.datasource.url=jdbc:mysql://localhost:3306/JWT?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
  6. spring.datasource.username=root
  7. spring.datasource.password=12345678
  8.  
  9. #mybatis扫描的包
  10. mybatis.type-aliases-package=com.ylc
  11. #mapper文件路径
  12. mybatis.mapper-locations=classpath:/**/*.xml
  13.  
  14. #开启sql打印日志 logging.level后面是mybatis对应的方法接口所在的包
  15. logging.level.com.ylc.jwtdemo.dao=debug

4、Entity包下新建一个User类

  1. import lombok.Data;
  2.  
  3. @Data
  4. public class User {
  5. private String username;
  6. private String password;
  7. private int id;
  8. }

5、Dao包下新建一个UserDao

  1. @Mapper
  2. public interface UserDao {
  3.  
  4. User login(User user);
  5. }

6、Service包下新建一个USerService

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

7、UseService的实现类UserServiceImp

  1. import java.util.HashMap;
  2. import java.util.Map;
  3.  
  4. @Service
  5. public class UserServiceImpI implements UserService {
  6.  
  7. @Autowired
  8. private UserDao userDao;
  9.  
  10. @Override
  11. public User login(User user) {
  12. User userdb=userDao.login(user);
  13. if(userdb!=null)
  14. {
  15. Map<String,String> map=new HashMap<>();
  16. map.put("name",userdb.getUsername());
  17. return userdb;
  18. }
  19. throw new RuntimeException("登录失败");
  20. }
  21. }

8、controller包下新建一个UserController

  1. @RestController
  2. public class UserController {
  3.  
  4. @Autowired
  5. private UserService userService;
  6.  
  7. @GetMapping("/user/login")
  8. public Map<String,Object> login(User user)
  9. {
  10. log.info("用户名:"+user.getUsername());
  11. log.info("密码:"+user.getPassword());
  12. Map<String,Object> map=new HashMap<>();
  13. try {
  14. userService.login(user);
  15. map.put("msg","登录成功");
  16. map.put("code","200");
  17. }
  18. catch (Exception ex)
  19. {
  20. map.put("msg","登录失败");
  21. }
  22.  
  23. return map;
  24. }
  25. }

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

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

10、JWT工具类JwtUtils

  1. /**
  2. * JWT工具类
  3. * @author yanglingcong
  4. * @date 2021/12/31 11:24 AM
  5. */
  6. public class JwtUtils {
  7. //鉴权 相当于私钥保存在服务器上
  8. private static final String secret="##@$%@#S#WS";
  9.  
  10.  
  11. /**
  12. * 生成token
  13. * @author yanglingcong
  14. * @date 2021/12/31 11:23 AM
  15. * @param map
  16. * @return String
  17. */
  18. public static String getToken(Map<String,String> map)
  19. {
  20. Calendar instance=Calendar.getInstance();
  21. //默认七天过期
  22. instance.add(Calendar.DATE,7);
  23. //创建JWT
  24. JWTCreator.Builder builder = JWT.create();
  25.  
  26. //payload
  27. map.forEach((k,v)->{
  28. builder.withClaim(k,v);
  29. });
  30. //指定令牌过期时间
  31. builder.withExpiresAt(instance.getTime());
  32.  
  33. String token=builder.sign(Algorithm.HMAC256(secret));
  34. return token;
  35. }
  36.  
  37. /**
  38. * 验证token
  39. * @author yanglingcong
  40. * @date 2021/12/31 11:26 AM
  41. * @param token
  42. */
  43. public static DecodedJWT verify(String token) {
  44. return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
  45. }
  46.  
  47. }

整个项目概览

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

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

引入JWT

  1. @Slf4j
  2. @RestController
  3. public class UserController {
  4.  
  5. @Autowired
  6. private UserService userService;
  7.  
  8. @GetMapping("/user/login")
  9. public Map<String,Object> login(User user)
  10. {
  11. log.info("用户名:"+user.getUsername());
  12. log.info("密码:"+user.getPassword());
  13. Map<String,Object> map=new HashMap<>();
  14. try {
  15. userService.login(user);
  16. map.put("msg","登录成功");
  17. map.put("code","200");
  18.  
  19. Map<String,String> payload=new HashMap<>();
  20. payload.put("name",user.getUsername());
  21. String token= JwtUtils.getToken(payload);
  22. map.put("token",token);
  23. }
  24. catch (Exception ex)
  25. {
  26. map.put("msg","登录失败");
  27. }
  28.  
  29. return map;
  30. }
  31.  
  32. @PostMapping("/test/verity")
  33. public Map<String,String> verityToken(String token)
  34. {
  35. Map<String, String> map=new HashMap<>();
  36. log.info("token为"+token);
  37. try {
  38. DecodedJWT verify = JwtUtils.verify(token);
  39. map.put("msg","验证成功");
  40. map.put("state","true");
  41. }
  42. catch (Exception exception)
  43. {
  44. map.put("msg","验证失败");
  45. exception.printStackTrace();
  46. }
  47. return map;
  48. }
  49. }

登录操作

访问: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

  1. /**
  2. * JWT拦截器
  3. * @author yanglingcong
  4. * @date 2021/12/31 12:39 PM
  5. */
  6. public class JwtInterceptor implements HandlerInterceptor {
  7. @Override
  8. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  9. HashMap<String, String> map=new HashMap<>();
  10. //从http请求头获取token
  11. String token = request.getHeader("token");
  12. try {
  13. //如果验证成功放行请求
  14. DecodedJWT verify = JwtUtils.verify(token);
  15. return true;
  16. }
  17. catch (Exception exception)
  18. {
  19. map.put("msg","验证失败:"+exception);
  20. }
  21. String json = new ObjectMapper().writeValueAsString(map);
  22. response.setContentType("application/json:charset=UTF=8");
  23. response.getWriter().println(json);
  24. return false;
  25. }
  26. }

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

  1. /**
  2. * @author yanglingcong
  3. */
  4. @Configuration
  5. public class InterceptConfig implements WebMvcConfigurer {
  6. @Override
  7. public void addInterceptors(InterceptorRegistry registry) {
  8. //添加拦截器
  9. registry.addInterceptor(new JwtInterceptor())
  10. //拦截的路径 需要进行token验证的路径
  11. .addPathPatterns("/test/verity")
  12. //放行的路径
  13. .excludePathPatterns("/user/login");
  14. }
  15. }

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

测试验证

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

总结

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

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

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号