關(guān)于JWT之token令牌認(rèn)證登錄
一.話題引入
在做項(xiàng)目過程中,我們一般都是最先編寫登錄注冊功能,登錄功能最重要的是登錄成功后,系統(tǒng)還會保存該登錄用戶信息,這種保存用戶信息的邏輯可以有兩種:
- 最簡單的一種就是使用Session來保存用戶信息,然后使用filter來驗(yàn)證用戶是否登錄,但是這種方法只能是單體架構(gòu)的項(xiàng)目適用,性能也不會很好。在分布式項(xiàng)目中,會有很多子模塊并且部署在不同的服務(wù)器中,這樣是無法使用session保存的,因?yàn)閟essio不能共享。
- 使用單點(diǎn)登錄技術(shù)就能很好地解決這個弊端。
- 單點(diǎn)登錄(Single Sign On)簡稱為 SSO。即在多個應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問所有相互信任的應(yīng)用系統(tǒng)。JWT是一種常用的單點(diǎn)登錄解決方案。
什么是JWT?
JWT是Json Web Token的簡稱,是一種令牌生成算法。使用JWT能夠保證Token的安全性,且能夠進(jìn)行Token時效性的檢驗(yàn)。使用JWT時,登錄成功后將用戶信息生成一串令牌字符串。將該字符串返回給客戶端,客戶端每次請求時都在請求頭攜帶該令牌字符串。在其他模塊驗(yàn)證令牌,通過則證明用戶處于登錄狀態(tài),并拿到解析后的用戶信息,未通過證明用戶處于未登錄狀態(tài)。
二.技術(shù)體現(xiàn)
要實(shí)現(xiàn)JWT鑒權(quán),就得實(shí)現(xiàn)如下步驟:
- 引入JWT工具類,編寫生成令牌和解析令牌的方法。
- 用戶登錄成功后生成令牌字符串返回給前端。
- 前端每次請求時都在請求頭帶入令牌字符串。
- 在通用模塊編寫攔截器,解析請求頭中的令牌字符串。
- 在Api模塊配置攔截器,配置攔截器攔截哪些接口,即這些接口需要登錄才能訪問。
現(xiàn)在來編寫代碼實(shí)現(xiàn)
2.1 引入依賴
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>2.2 編寫JWT工具類
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.itbaizhan.shopping_common.exception.BusException;
import com.itbaizhan.shopping_common.pojo.ShoppingUser;
import com.itbaizhan.shopping_common.result.CodeEnum;
import java.util.Date;
public class JWTUtil {
//token過期時間,一天
private static final Long EXPIRE_DATE = 1000*60*60*24L;
// 秘鑰
private static final String SECRET = "jackie";
// 簽發(fā)者
private static final String ISSUER = "JACKIE";
/**
* 簽名生成
* @param shoppingUser
* @return
*/
public static String sign(ShoppingUser shoppingUser){
String token = JWT.create()
.withIssuer(ISSUER) // 簽發(fā)者
.withIssuedAt(new Date()) // 簽發(fā)時間
.withExpiresAt(new Date(new Date().getTime() + EXPIRE_DATE)) // 過期時間
.withSubject(shoppingUser.getUsername()) // 保存用戶名
.withClaim("userId",shoppingUser.getId()) // 保存用戶id
.sign(Algorithm.HMAC256(SECRET)); // 秘鑰
return token;
}
/**
* 簽名解析
* @param token 簽名字符串
* @return 解析得出的用戶名
*/
public static String verify(String token){
try {
String username = JWT
.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISSUER)
.build()
.verify(token)
.getSubject();
return username;
} catch (Exception e){
throw new BusException(CodeEnum.VERIFY_TOKEN_ERROR);
}
}
/**
* 簽名解析,獲取用戶id
* @param token 簽名字符串
* @return 用戶id
*/
public static Long getId(String token){
try {
Long userId = JWT
.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISSUER)
.build()
.verify(token)
.getClaim("userId")
.asLong();
return userId;
} catch (Exception e){
throw new BusException(CodeEnum.VERIFY_TOKEN_ERROR);
}
}
}這個utils有三個方法,一個是生成token字符串,一個是解析該字符串獲取登錄的用戶名,還要就是獲取登錄用戶的id。
2.3 編寫登錄方法
如果使用Session存儲用戶信息,在驗(yàn)證完名字和密碼后,直接將該登錄對象setAttribute(“users”,users)里面。

而使用單點(diǎn)登錄,則是直接調(diào)用JWTUtil.sign(user),生成JWT令牌,返回該令牌給前端用戶。 標(biāo)準(zhǔn)代碼:
服務(wù)層
@Override
public String loginPassword(String username, String password) {
// 1.驗(yàn)證用戶名
QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();
queryWrapper.eq("username",username);
ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);
if (shoppingUser == null){
throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);
}
// 2.驗(yàn)證密碼
boolean verify = Md5Util.verify(password, shoppingUser.getPassword());
if (!verify){
throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);
}
// 3.生成JWT令牌,返回令牌
String sign = JWTUtil.sign(shoppingUser);
return sign;
}控制層
/**
* 用戶名密碼登錄
* @param shoppingUser 用戶對象
* @return 登錄結(jié)果
*/
@PostMapping("/loginPassword")
public BaseResult loginPassword(@RequestBody ShoppingUser shoppingUser){
String sign = shoppingUserService.loginPassword(shoppingUser.getUsername(), shoppingUser.getPassword());
return BaseResult.ok(sign);
}2.4 編寫JWT攔截器驗(yàn)證令牌
這里驗(yàn)證令牌的方式是攔截所有的請求,如果 JWTUtil.verify(token)不拋異常則通過這個請求。
// 攔截器,驗(yàn)證令牌
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 獲取請求頭中的token
String token = request.getHeader("token");
// 驗(yàn)證令牌
JWTUtil.verify(token);
return true;
}
}2.5 編寫要配置攔截的接口
我們在用戶模塊配置該模塊要攔截的接口(如果是單體架構(gòu),攔截器和該部分可以寫在一起)。
除了這種方式,還有一種編寫方式,你想知道嗎?
在User模塊先實(shí)例化一個InterceptorConfig配置類,實(shí)現(xiàn)WebMvcConfigurer接口,將上面寫的攔截器在addInterceptor(new JWTInterceptor())方法里面實(shí)例化,然后攔截所有的接口( .addPathPatterns(“/**”)),再放行不需要認(rèn)證的接口。
// 攔截器配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**") // 攔截的接口
.excludePathPatterns(
"填寫需要放行的接口url"
); //放行的接口
}
}至此,一個令牌認(rèn)證就完成啦。
到此這篇關(guān)于關(guān)于JWT之token令牌認(rèn)證登錄的文章就介紹到這了,更多相關(guān)token令牌認(rèn)證登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Springboot不能自動提交數(shù)據(jù)庫連接問題
在使用SSM框架開發(fā)時,若在同一Service內(nèi)部方法間互相調(diào)用,直接使用this關(guān)鍵字會導(dǎo)致事務(wù)管理失效,從而引發(fā)如數(shù)據(jù)庫連接不足等問題,原因是通過this調(diào)用不會經(jīng)過Spring的代理,因此不會自動進(jìn)行事務(wù)處理2024-09-09
Java通過python命令執(zhí)行DataX任務(wù)的實(shí)例
今天小編就為大家分享一篇Java通過python命令執(zhí)行DataX任務(wù)的實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08
分析xxljob登入功能集成OIDC的統(tǒng)一認(rèn)證
這篇文章主要為大家介紹分析xxljob登入功能集成OIDC的統(tǒng)一認(rèn)證的詳解說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02

