此篇文章的內容也是學習不久,終於到週末有時間碼一篇文章分享知識追尋者的粉絲們,學完本篇文章,讀者將對token類的登錄認證流程有個全面的瞭解,能夠動態搭建本身的登錄認證過程;對小項目而已經是個輕量級的認證機制,符合開發需求;更多精彩原創內容關注公主號知識追尋者,讀者的確定,就是對做者的創做的最大支持;java
JWT的token由三段信息構成的,將這三段信息文本用.
鏈接一塊兒就構成了JWT字符串;web
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg
URL
,POST
參數或者在HTTP header
發送,數據量小,傳輸速度快Token
是以JSON
加密的形式保存在客戶端的,因此JWT
是跨語言支持;項目構件以下算法
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
jwt工具類中有三個方法,分別是生成數字簽名用於用戶首次登錄時發送jwt給客戶端;其次是校驗方法,用於攔截器攔截全部規則內的url,每一個請求都必須帶有服務器發送的jwt,通過驗證後才放行請求;最後一個得到用戶名的方法用於查詢密鑰,在驗證jwt時做爲參數傳入;spring
/* * * @Author lsc * <p> JWT工具類 </p> * @Param * @Return */ public class JwtUtil { // Token過時時間30分鐘 public static final long EXPIRE_TIME = 30 * 60 * 1000; /* * * @Author lsc * <p> 校驗token是否正確 </p> * @Param token * @Param username * @Param secret * @Return boolean */ public static boolean verify(String token, String username, String secret) { try { // 設置加密算法 Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm) .withClaim("username", username) .build(); // 效驗TOKEN DecodedJWT jwt = verifier.verify(token); return true; } catch (Exception exception) { return false; } } /* * * @Author lsc * <p>生成簽名,30min後過時 </p> * @Param [username, secret] * @Return java.lang.String */ public static String sign(String username, String secret) { Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); Algorithm algorithm = Algorithm.HMAC256(secret); // 附帶username信息 return JWT.create() .withClaim("username", username) .withExpiresAt(date) .sign(algorithm); } /* * * @Author lsc * <p> 得到用戶名 </p> * @Param [request] * @Return java.lang.String */ public static String getUserNameByToken(HttpServletRequest request) { String token = request.getHeader("token"); DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("username") .asString(); } }
實體中包含用戶名,和密碼,一切從簡;數據庫
/** * @Author lsc * <p> </p> */ @Data public class SysUser { private String username; private String password; }
表現層代碼用戶用戶登錄認證,認證成功後發放token給客戶端;api
/** * @Author lsc * <p> </p> */ @RestController public class SysUserController { @PostMapping(value = "/login") public Map<String, Object> login(@RequestBody SysUser sysUser){ Map<String, Object> map = new HashMap<>(); String username = sysUser.getUsername(); String password = sysUser.getPassword(); // 省略 帳號密碼驗證 // 驗證成功後發送token String token = JwtUtil.sign(username,password); if (token != null){ map.put("code", "200"); map.put("message","認證成功"); map.put("token", token); return map; } map.put("code", "403"); map.put("message","認證失敗"); return map; } }
測試url http://localhost:8080/login跨域
postman post請求測試參數以下瀏覽器
{ "username": "zszxz", "password": "zszxz" }
返回內容以下springboot
{ "code": "200", "message": "認證成功", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg" }
基於前面已經實現jwt登陸認證後發放token給客戶端;本節內容就是將token放入請求頭中發送請求給服務器;服務器使用攔截器攔截請求對token進行驗證;驗證成功請求經過,不然請求資源失敗;服務器
自定義攔截器JwtInterceptor,實現HandlerInterceptor接口,每次請求到達以前都會驗證token是否有效;
/** * @Author lsc * <p>token驗證攔截器 </p> */ @Component public class JwtInterceptor implements HandlerInterceptor { @Autowired SysUserService sysUserService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 從 http 請求頭中取出 token String token = request.getHeader("token"); // 若是不是映射到方法直接經過 if(!(handler instanceof HandlerMethod)){ return true; } if (token != null){ String username = JwtUtil.getUserNameByToken(request); // 這邊拿到的 用戶名 應該去數據庫查詢得到密碼,簡略,步驟在service直接獲取密碼 boolean result = JwtUtil.verify(token,username,sysUserService.getPassword()); if(result){ System.out.println("經過攔截器"); return true; } } return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
/** * @Author lsc * <p> 模擬查詢數據庫得到帳號密碼 </p> */ @Service public class SysUserService { public String getPassword(){ return "zszxz"; } }
攔截器配置中主要定義攔截請求規則,將攔截器注入WebMvcConfigurer;cors跨域處理;
/* * * @Author lsc * <p>攔截器配置 </p> * @Param * @Return */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { /* * * @Author lsc * <p> 設置攔截路徑 </p> * @Param [registry] * @Return void */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/login"); } /* * * @Author lsc * <p> 將攔截器注入context </p> * @Param [] * @Return com.zszxz.jwt.interceptor.JwtInterceptor */ @Bean public JwtInterceptor authenticationInterceptor() { return new JwtInterceptor(); } /* * * @Author lsc * <p>跨域支持 </p> * @Param [registry] * @Return void */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD") .maxAge(3600 * 24); } }
表現層接口用於攔截親求測試
/** * @Author lsc * <p> </p> */ @RestController public class TestController { @GetMapping(value = "/api/test") public String get(){ return "zszxz"; } }
測試url http://localhost:8080/api/test
發送get請求給服務器,帶有請求頭,key爲token,value爲用戶首次登錄時返回的token串;
測試返回內容以下
zszxz
https://jwt.io/introduction/
源碼 關注公主號或者做者專欄說明便可得到;