SpringBoot集成JWT令牌詳細說明
一,傳統(tǒng)Cookie+Session與JWT對比
1, 在傳統(tǒng)的用戶登錄認證中,因為http是無狀態(tài)的,所以都是采用session方式。用戶登錄成功,服務端會保證一個session,當然會給客戶端一個sessionId,客戶端會把sessionId保存在cookie中,每次請求都會攜帶這個sessionId。
2,cookie+session這種模式通常是保存在內(nèi)存中,而且服務從單服務到多服務會面臨的session共享問題,隨著用戶量的增多,開銷就會越大。而JWT不是這樣的,只需要服務端生成token,客戶端保存這個token,每次請求攜帶這個token,服務端認證解析就可。
3, JWT方式校驗方式更加簡單便捷化,無需通過redis緩存,而是直接根據(jù)token取出保存的用戶信息,以及對token可用性校驗,單點登錄,驗證token更為簡單。
二,springboot集成jwt
1,jwt的整合依賴
<!-- JWT依賴 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
2,jwt的自定義配置
server: port: 8080 spring: application: name: springboot-jwt # 自定義配置 config: jwt: # 加密密鑰 secret: abcdefg1234567 # token有效時長 expire: 3600 # header 名稱 header: token
3,編寫JwtConfig
JwtConfig負責
- 生成token
- 獲取token中的注冊信息
- 驗證token是否過期失效
- 獲取token失效時間
- 從token中獲取用戶名
- 獲取jwt發(fā)布時間
package com.ftx.jwt.config; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName JwtConfig.java * @Description TODO * @createTime 2020年06月22日 15:43:00 */ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Date; /** * JWT的token,區(qū)分大小寫 */ @ConfigurationProperties(prefix = "config.jwt", ignoreUnknownFields = true) @Component public class JwtConfig { private String secret; private long expire; private String header; /** * 生成token * @param subject * @return */ public String createToken (String subject){ Date nowDate = new Date(); Date expireDate = new Date(nowDate.getTime() + expire * 1000);//過期時間 return Jwts.builder() .setHeaderParam("typ", "JWT") .setSubject(subject) .setIssuedAt(nowDate) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } /** * 獲取token中注冊信息 * @param token * @return */ public Claims getTokenClaim (String token) { try { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); }catch (Exception e){ // e.printStackTrace(); return null; } } /** * 驗證token是否過期失效 * @param expirationTime * @return */ public boolean isTokenExpired (Date expirationTime) { return expirationTime.before(new Date()); } /** * 獲取token失效時間 * @param token * @return */ public Date getExpirationDateFromToken(String token) { return getTokenClaim(token).getExpiration(); } /** * 獲取用戶名從token中 */ public String getUsernameFromToken(String token) { return getTokenClaim(token).getSubject(); } /** * 獲取jwt發(fā)布時間 */ public Date getIssuedAtDateFromToken(String token) { return getTokenClaim(token).getIssuedAt(); } // --------------------- getter & setter --------------------- public String getSecret() { return secret; } public void setSecret(String secret) { this.secret = secret; } public long getExpire() { return expire; } public void setExpire(long expire) { this.expire = expire; } public String getHeader() { return header; } public void setHeader(String header) { this.header = header; } }
4,配置攔截器
在攔截器中對token進行驗證。
package com.ftx.jwt.config; import io.jsonwebtoken.Claims; import io.jsonwebtoken.SignatureException; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName TokenInterceptor.java * @Description TODO * @createTime 2020年06月22日 15:47:00 */ @Component public class TokenInterceptor extends HandlerInterceptorAdapter { @Resource private JwtConfig jwtConfig ; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws SignatureException { /** 地址過濾 */ String uri = request.getRequestURI() ; if (uri.contains("/login")){ return true ; } /** Token 驗證 */ String token = request.getHeader(jwtConfig.getHeader()); if(StringUtils.isEmpty(token)){ token = request.getParameter(jwtConfig.getHeader()); } if(StringUtils.isEmpty(token)){ throw new SignatureException(jwtConfig.getHeader()+ "不能為空"); } Claims claims = null; try{ claims = jwtConfig.getTokenClaim(token); if(claims == null || jwtConfig.isTokenExpired(claims.getExpiration())){ throw new SignatureException(jwtConfig.getHeader() + "失效,請重新登錄。"); } }catch (Exception e){ throw new SignatureException(jwtConfig.getHeader() + "失效,請重新登錄。"); } //該token可用,放行 return true; } }
注冊攔截器到SpringMvc
package com.ftx.jwt.config; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName WebConfig.java * @Description TODO * @createTime 2020年06月22日 15:53:00 */ import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.config.annotation.*; import javax.annotation.Resource; import java.util.List; @Configuration public class WebConfig implements WebMvcConfigurer { @Resource private TokenInterceptor tokenInterceptor ; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor).addPathPatterns("/**"); } }
5,編寫測試controller接口
package com.ftx.jwt.controller; import com.alibaba.fastjson.JSONObject; import com.ftx.jwt.config.JwtConfig; import com.ftx.jwt.util.ResultTool; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName TokenController.java * @Description TODO * @createTime 2020年06月22日 16:05:00 */ @RestController public class TokenController { @Resource private JwtConfig jwtConfig ; /** * 用戶登錄接口 * @param userName * @param passWord * @return */ @PostMapping("/login") public JSONObject login (@RequestParam("userName") String userName, @RequestParam("passWord") String passWord){ JSONObject json = new JSONObject(); /** 驗證userName,passWord和數(shù)據(jù)庫中是否一致,如不一致,直接return ResultTool.errer(); 【這里省略該步驟】*/ // 這里模擬通過用戶名和密碼,從數(shù)據(jù)庫查詢userId // 這里把userId轉(zhuǎn)為String類型,實際開發(fā)中如果subject需要存userId,則可以JwtConfig的createToken方法的參數(shù)設置為Long類型 String userId = 5 + ""; String token = jwtConfig.createToken(userId) ; if (!StringUtils.isEmpty(token)) { json.put("token",token) ; } return ResultTool.success(json) ; } /** * 需要 Token 驗證的接口 */ @PostMapping("/info") public JSONObject info (){ return ResultTool.success("info") ; } /** * 根據(jù)請求頭的token獲取userId * @param request * @return */ @GetMapping("/getUserInfo") public JSONObject getUserInfo(HttpServletRequest request){ String usernameFromToken = jwtConfig.getUsernameFromToken(request.getHeader("token")); return ResultTool.success(usernameFromToken) ; } }
用PostMan測試工具測試一下,訪問登錄接口,當對賬號密碼驗證通過時,則返回一個token給客戶端 說明:token是在請求頭處,request.getHeader()得到token。
當直接去訪問info接口時,會返回token為空的異常 當在請求頭加上正確token時,則攔截器驗證通過,可以正常訪問到接口
當在請求頭加入一個錯誤token,則會返回token失效的異常 接下來測試一下獲取用戶信息,因為這里存的subject為userId,所以直接返回上面寫死的假數(shù)據(jù)5
三,知識點概述
1,@Resource注解
- @Autowired與@Resource都可以用來裝配bean. 都可以寫在字段上,或?qū)懺趕etter方法上。
- @Autowired默認按類型裝配(這個注解是屬于spring的),默認情況下必須要求依賴對象必須存在,如果要允許null值,可以設置它的required屬性為false,如:@Autowired(required=false) 3,@Resource(這個注解屬于J2EE的),默認按照名稱進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,當注解寫在字段上時,默認取字段名進行安裝名稱查找,如果注解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
2,@ConfigurationProperties注解
@ConfigurationProperties:告訴SpringBoot將本類中的所有屬性和配置文件中相關的配置進行綁定;prefix = "xxx":配置文件中哪個下面的所有屬性進行一一映射
到此這篇關于SpringBoot集成JWT令牌詳細說明的文章就介紹到這了,更多相關SpringBoot集成JWT令牌內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MyBatis中RowBounds實現(xiàn)內(nèi)存分頁
RowBounds是MyBatis提供的一種內(nèi)存分頁方式,適用于小數(shù)據(jù)量的分頁場景,本文就來詳細的介紹一下,具有一定的參考價值,感興趣的可以了解一下2024-12-12java abstract class interface之間的區(qū)別介紹
含有abstract修飾符的class即為抽象類,abstract 類不能創(chuàng)建的實例對象,abstract class類中定義抽象方法必須在具體(Concrete)子類中實現(xiàn),所以,不能有抽象構造方法或抽象靜態(tài)方法2012-11-11SpringBoot動態(tài)定時任務、動態(tài)Bean、動態(tài)路由詳解
這篇文章主要介紹了SpringBoot動態(tài)定時任務、動態(tài)Bean、動態(tài)路由詳解,之前用過Spring中的定時任務,通過@Scheduled注解就能快速的注冊一個定時任務,但有的時候,我們業(yè)務上需要動態(tài)創(chuàng)建,或者根據(jù)配置文件、數(shù)據(jù)庫里的配置去創(chuàng)建定時任務,需要的朋友可以參考下2023-10-10Mybatis之@ResultMap,@Results,@Result注解的使用
這篇文章主要介紹了Mybatis之@ResultMap,@Results,@Result注解的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12