SpringBoot集成JWT實(shí)現(xiàn)token驗(yàn)證
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
66套java從入門到精通實(shí)戰(zhàn)課程分享
優(yōu)點(diǎn)
簡(jiǎn)潔: 可以通過(guò)
URL、POST參數(shù)或者在HTTP header發(fā)送,因?yàn)閿?shù)據(jù)量小,傳輸速度也很快;自包含:負(fù)載中可以包含用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫(kù);
因?yàn)?/span>
Token是以JSON加密的形式保存在客戶端的,所以JWT是跨語(yǔ)言的,原則上任何web形式都支持;不需要在服務(wù)端保存會(huì)話信息,特別適用于分布式微服務(wù)。
缺點(diǎn)
無(wú)法作廢已頒布的令牌;
不易應(yīng)對(duì)數(shù)據(jù)過(guò)期。
一、Jwt消息構(gòu)成
1.1 組成
頭部(
header)載荷(
payload)簽證(
signature)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxYzdiY2IzMS02ODFlLTRlZGYtYmU3Yy0wOTlkODAzM2VkY2UiLCJleHAiOjE1Njk3Mjc4OTF9.wweMzyB3tSQK34Jmez36MmC5xpUh15Ni3vOV_SGCzJ8
1.2 header
聲明類型,這里是
Jwt聲明加密的算法 通常直接使用?
HMAC SHA256
| JWS | 算法名稱 |
|---|---|
| HS256 | HMAC256 |
| HS384 | HMAC384 |
| HS512 | HMAC512 |
| RS256 | RSA256 |
| RS384 | RSA384 |
| RS512 | RSA512 |
| ES256 | ECDSA256 |
| ES384 | ECDSA384 |
| ES512 | ECDSA512 |
1.3 playload
標(biāo)準(zhǔn)中注冊(cè)的聲明的數(shù)據(jù);
自定義數(shù)據(jù)。
標(biāo)準(zhǔn)中注冊(cè)的聲明 (建議但不強(qiáng)制使用)
iss:?jwt簽發(fā)者
sub:?jwt所面向的用戶
aud:?接收jwt的一方
exp:?jwt的過(guò)期時(shí)間,這個(gè)過(guò)期時(shí)間必須要大于簽發(fā)時(shí)間
nbf:?定義在什么時(shí)間之前,該jwt都是不可用的.
iat:?jwt的簽發(fā)時(shí)間
jti: jwt的唯一身份標(biāo)識(shí),主要用來(lái)作為一次性token,從而回避重放攻擊。
自定義數(shù)據(jù):存放我們想放在
token中存放的key-value值
1.4 signature
二、Spring Boot和Jwt集成示例
2.1、項(xiàng)目依賴 pom.xml
??
???com.auth0
???java-jwt
???3.10.3
??
2.2、自定義注解
//需要登錄才能進(jìn)行操作的注解LoginToken?
@Target({ElementType.METHOD,?ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public?@interface?LoginToken?{
????boolean?required()?default?true;
}
//用來(lái)跳過(guò)驗(yàn)證的PassToken
@Target({ElementType.METHOD,?ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public?@interface?PassToken?{
????boolean?required()?default?true;
}
2.3、用戶實(shí)體類、及查詢service
@Data
@AllArgsConstructor
@NoArgsConstructor
public?class?User?{
????private?String?userID;
????private?String?userName;
????private?String?passWord;
}
package?com.sky.springbootdemo.jwt.service;
?
import?com.sky.springbootdemo.jwt.entity.User;
import?org.springframework.stereotype.Service;
?
/**
?*?@title:?UserService
?*?@Author?gjt
?*?@Date:?2020-12-21
?*?@Description:
?*/
@Service
public?class?UserService?{
?
????public?User?getUser(String?userid,?String?password){
????????if?("admin".equals(userid)?&&?"admin".equals(password)){
????????????User?user=new?User();
????????????user.setUserID("admin");
????????????user.setUserName("admin");
????????????user.setPassWord("admin");
????????????return?user;
????????}
????????else{
????????????return?null;
????????}
????}
?
????public?User?getUser(String?userid){
????????if?("admin".equals(userid)){
????????????User?user=new?User();
????????????user.setUserID("admin");
????????????user.setUserName("admin");
????????????user.setPassWord("admin");
????????????return?user;
????????}
????????else{
????????????return?null;
????????}
????}
}
2.4、Token生成
package?com.sky.springbootdemo.jwt.service;
?
import?com.auth0.jwt.JWT;
import?com.auth0.jwt.algorithms.Algorithm;
import?com.sky.springbootdemo.jwt.entity.User;
import?org.springframework.stereotype.Service;
?
import?java.util.Date;
?
/**
?*?@title:?TokenService
?*?@Author?gjt
?*?@Date:?2020-12-21
?*?@Description:
?*/
@Service
public?class?TokenService?{
????/**
?????*?過(guò)期時(shí)間5分鐘
?????*/
????private?static?final?long?EXPIRE_TIME?=?5?*?60?*?1000;
?
????public?String?getToken(User?user)?{
????????Date?date?=?new?Date(System.currentTimeMillis()?+?EXPIRE_TIME);
????????String?token="";
????????token=?JWT.create().withAudience(user.getUserID())?//?將?user?id?保存到?token?里面
????????????.withExpiresAt(date)?//五分鐘后token過(guò)期
????????????.sign(Algorithm.HMAC256(user.getPassWord()));?//?以?password?作為?token?的密鑰
????????return?token;
????}
}
2.5、攔截器攔截token
package?com.sky.springbootdemo.jwt.interceptor;
?
import?com.auth0.jwt.JWT;
import?com.auth0.jwt.JWTVerifier;
import?com.auth0.jwt.algorithms.Algorithm;
import?com.auth0.jwt.exceptions.JWTDecodeException;
import?com.auth0.jwt.exceptions.JWTVerificationException;
import?com.sky.springbootdemo.jwt.annotation.LoginToken;
import?com.sky.springbootdemo.jwt.annotation.PassToken;
import?com.sky.springbootdemo.jwt.entity.User;
import?com.sky.springbootdemo.jwt.service.UserService;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.web.method.HandlerMethod;
import?org.springframework.web.servlet.HandlerInterceptor;
import?org.springframework.web.servlet.ModelAndView;
?
import?javax.servlet.http.HttpServletRequest;
import?javax.servlet.http.HttpServletResponse;
import?java.lang.reflect.Method;
?
/**
?*?@title:?JwtInterceptor
?*?@Author?gjt
?*?@Date:?2020-12-21
?*?@Description:
?*/
public?class?JwtInterceptor?implements?HandlerInterceptor{
?
????@Autowired
????private?UserService?userService;
?
????@Override
????public?boolean?preHandle(HttpServletRequest?httpServletRequest,?HttpServletResponse?httpServletResponse,?Object?object)?throws?Exception?{
????????String?token?=?httpServletRequest.getHeader("token");//?從?http?請(qǐng)求頭中取出?token
????????//?如果不是映射到方法直接通過(guò)
????????if(!(object?instanceof?HandlerMethod)){
????????????return?true;
????????}
????????HandlerMethod?handlerMethod=(HandlerMethod)object;
????????Method?method=handlerMethod.getMethod();
????????//檢查是否有passtoken注釋,有則跳過(guò)認(rèn)證
????????if?(method.isAnnotationPresent(PassToken.class))?{
????????????PassToken?passToken?=?method.getAnnotation(PassToken.class);
????????????if?(passToken.required())?{
????????????????return?true;
????????????}
????????}
????????//檢查有沒(méi)有需要用戶權(quán)限的注解
????????if?(method.isAnnotationPresent(LoginToken.class))?{
????????????LoginToken?loginToken?=?method.getAnnotation(LoginToken.class);
????????????if?(loginToken.required())?{
????????????????//?執(zhí)行認(rèn)證
????????????????if?(token?==?null)?{
????????????????????throw?new?RuntimeException("無(wú)token,請(qǐng)重新登錄");
????????????????}
????????????????//?獲取?token?中的?user?id
????????????????String?userId;
????????????????try?{
????????????????????userId?=?JWT.decode(token).getAudience().get(0);
????????????????}?catch?(JWTDecodeException?j)?{
????????????????????throw?new?RuntimeException("401");
????????????????}
????????????????User?user?=?userService.getUser(userId);
????????????????if?(user?==?null)?{
????????????????????throw?new?RuntimeException("用戶不存在,請(qǐng)重新登錄");
????????????????}
????????????????//?驗(yàn)證?token
????????????????JWTVerifier?jwtVerifier?=?JWT.require(Algorithm.HMAC256(user.getPassWord())).build();
????????????????try?{
????????????????????jwtVerifier.verify(token);
????????????????}?catch?(JWTVerificationException?e)?{
????????????????????throw?new?RuntimeException("401");
????????????????}
????????????????return?true;
????????????}
????????}
????????return?true;
????}
?
????@Override
????public?void?postHandle(HttpServletRequest?httpServletRequest,?HttpServletResponse?httpServletResponse,?Object?o,?ModelAndView?modelAndView)?throws?Exception?{
?
????}
????@Override
????public?void?afterCompletion(HttpServletRequest?httpServletRequest,?HttpServletResponse?httpServletResponse,?Object?o,?Exception?e)?throws?Exception?{
?
????}
}
2.6、注冊(cè)攔截器
package?com.sky.springbootdemo.jwt.config;
?
import?com.sky.springbootdemo.jwt.interceptor.JwtInterceptor;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import?org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
?
/**
?*?@title:?InterceptorConfig
?*?@Author?gjt
?*?@Date:?2020-12-21
?*?@Description:
?*/
@Configuration
public?class?InterceptorConfig?implements?WebMvcConfigurer{
????@Override
????public?void?addInterceptors(InterceptorRegistry?registry)?{
????????registry.addInterceptor(jwtInterceptor())
????????????.addPathPatterns("/**");????//?攔截所有請(qǐng)求,通過(guò)判斷是否有?@LoginRequired?注解?決定是否需要登錄
?
????????//注冊(cè)TestInterceptor攔截器
//????????InterceptorRegistration?registration?=?registry.addInterceptor(jwtInterceptor());
//????????registration.addPathPatterns("/**");??????????????????????//添加攔截路徑
//????????registration.excludePathPatterns(?????????????????????????//添加不攔截路徑
//????????????"/**/*.html",????????????//html靜態(tài)資源
//????????????"/**/*.js",??????????????//js靜態(tài)資源
//????????????"/**/*.css",?????????????//css靜態(tài)資源
//????????????"/**/*.woff",
//????????????"/**/*.ttf",
//????????????"/swagger-ui.html"
//????????);
????}
????@Bean
????public?JwtInterceptor?jwtInterceptor()?{
????????return?new?JwtInterceptor();
????}
}
?2.7、登錄Controller
package?com.sky.springbootdemo.jwt.controller;
?
import?com.alibaba.fastjson.JSONObject;
import?com.sky.springbootdemo.jwt.annotation.LoginToken;
import?com.sky.springbootdemo.jwt.entity.User;
import?com.sky.springbootdemo.jwt.service.TokenService;
import?com.sky.springbootdemo.jwt.service.UserService;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.web.bind.annotation.PostMapping;
import?org.springframework.web.bind.annotation.RestController;
?
/**
?*?@title:?Login
?*?@Author?gjt
?*?@Date:?2020-12-22
?*?@Description:
?*/
@RestController
public?class?Login?{
?
????@Autowired
????private?UserService?userService;
????@Autowired
????private?TokenService?tokenService;
?
????@PostMapping("login")
????public?Object?login(String?username,?String?password){
????????JSONObject?jsonObject=new?JSONObject();
????????User?user=userService.getUser(username,?password);
????????if(user==null){
????????????jsonObject.put("message","登錄失敗!");
????????????return?jsonObject;
????????}else?{
????????????String?token?=?tokenService.getToken(user);
????????????jsonObject.put("token",?token);
????????????jsonObject.put("user",?user);
????????????return?jsonObject;
????????}
????}
?
????@LoginToken
????@PostMapping("/getMessage")
????public?String?getMessage(){
????????return?"你已通過(guò)驗(yàn)證";
????}
}
2.8、配置全局異常捕獲
package?com.sky.springbootdemo.jwt.controller;
?
import?com.alibaba.fastjson.JSONObject;
import?org.springframework.web.bind.annotation.ExceptionHandler;
import?org.springframework.web.bind.annotation.ResponseBody;
import?org.springframework.web.bind.annotation.RestControllerAdvice;
?
/**
?*?@title:?GlobalExceptionHandler
?*?@Author?gjt
?*?@Date:?2020-12-22
?*?@Description:
?*/
@RestControllerAdvice
public?class?GlobalExceptionHandler?{
????@ResponseBody
????@ExceptionHandler(Exception.class)
????public?Object?handleException(Exception?e)?{
????????String?msg?=?e.getMessage();
????????if?(msg?==?null?||?msg.equals(""))?{
????????????msg?=?"服務(wù)器出錯(cuò)";
????????}
????????JSONObject?jsonObject?=?new?JSONObject();
????????jsonObject.put("code",?1000);
????????jsonObject.put("message",?msg);
????????return?jsonObject;
????}
}
三、postman測(cè)試
3.1、獲取tockem
3.2、無(wú)tocken登錄
3.3、有tocken登錄
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。
本文鏈接:
https://blog.csdn.net/gjtao1130/article/details/111658060
粉絲福利:Java從入門到入土學(xué)習(xí)路線圖
???

?長(zhǎng)按上方微信二維碼?2 秒
感謝點(diǎn)贊支持下哈?




