實戰(zhàn)SpringBoot集成JWT實現(xiàn)token驗證
JWT可以理解為一個加密的字符串,里面由三部分組成:頭部(Header)、負(fù)載(Payload)、簽名(signature)
由base64加密后的header和payload使用.連接組成的字符串,然后通過header中聲明的加密方式進(jìn)行加鹽secret組合加密,然后就構(gòu)成了JWT字符串
往期介紹了JWT相關(guān)概念以及基本操作,接下來介紹如何在SpringBoot中整合JWT實現(xiàn)登陸注冊
環(huán)境搭建
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數(shù)據(jù)庫連接池--> <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、數(shù)據(jù)庫結(jié)構(gòu)
有一個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對應(yīng)的方法接口所在的包 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的實現(xiàn)類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 { //鑒權(quán) 相當(dāng)于私鑰保存在服務(wù)器上 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(); //默認(rèn)七天過期 instance.add(Calendar.DATE,7); //創(chuàng)建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); } }
整個項目概覽
測試驗證是否能夠連通數(shù)據(jù)庫
訪問: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中,業(yè)務(wù)邏輯是不能放在Controller層中的。假如很多接口都需要token來進(jìn)行驗證保護(hù),那每一個接口都需要添加這樣一段代碼,造成代碼冗余。
程序優(yōu)化
如果是web項目使用攔截器進(jìn)行優(yōu)化,如果是springcloud項目在網(wǎng)關(guān)層進(jìn)行攔截,下面演示如何使用攔截器攔截
最好還把JWT生成token放在http請求頭,這樣就不需要把token當(dāng)成參數(shù)傳遞了
新建一個攔截器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()) //攔截的路徑 需要進(jìn)行token驗證的路徑 .addPathPatterns("/test/verity") //放行的路徑 .excludePathPatterns("/user/login"); } }
登錄是不需要攔截的,其他請求如果有需要驗證token就放入攔截器的路徑
測試驗證
在http請求頭中放入token,會被攔截器攔截驗證token的有效性
總結(jié)
這就是SpringBoot整合JWT實際項目一個大概的流程,但是細(xì)節(jié)方面secret(私鑰)肯定每個用戶都是不一樣的,這里給寫死了,而且私鑰得保存在一個安全的地方。包括payload部分不能存放敏感的密碼信息等等,還可以進(jìn)行優(yōu)化。
項目代碼:https://gitee.com/yanglingcong/jwt-demo
到此這篇關(guān)于實戰(zhàn)SpringBoot集成JWT實現(xiàn)token驗證的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
IntelliJ IDEA使用快捷鍵重命名項目、變量、文件等方法總結(jié)
今天小編就為大家分享一篇關(guān)于IntelliJ IDEA使用快捷鍵重命名項目、變量、文件等方法總結(jié),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10nacos服務(wù)注冊服務(wù)發(fā)現(xiàn)依賴配置詳解
這篇文章主要為大家介紹了nacos服務(wù)注冊服務(wù)發(fā)現(xiàn)依賴配置詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09Javaweb實戰(zhàn)之實現(xiàn)蛋糕訂購系統(tǒng)
隨著網(wǎng)絡(luò)的普及與發(fā)展,網(wǎng)上購物逐漸成為一種主流消費的方式。這篇文章主要介紹了通過JavaWeb制作一個線上蛋糕訂購系統(tǒng),文中示例代碼講解詳細(xì),需要的朋友可以參考一下2021-12-12Hibernate實現(xiàn)批量添加數(shù)據(jù)的方法
這篇文章主要介紹了Hibernate實現(xiàn)批量添加數(shù)據(jù)的方法,詳細(xì)分析了基于Hibernate執(zhí)行批量添加操作的具體步驟與相關(guān)實現(xiàn)代碼,需要的朋友可以參考下2016-03-03java實現(xiàn)新浪微博Oauth接口發(fā)送圖片和文字的方法
這篇文章主要介紹了java實現(xiàn)新浪微博Oauth接口發(fā)送圖片和文字的方法,涉及java調(diào)用新浪微博Oauth接口的使用技巧,具有一定參考接借鑒價值,需要的朋友可以參考下2015-07-07使用java實現(xiàn)http多線程斷點下載文件(一)
Java 多線程斷點下載文件基本原理:利用URLConnection獲取要下載文件的長度、頭部等相關(guān)信息,并設(shè)置響應(yīng)的頭部信息,本文將詳細(xì)介紹,需要了解更多的朋友可以參考下2012-12-12