實戰SpringBoot集成JWT實現token驗證
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驗證的文章就介紹到這瞭。希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。