springBoot整合jwt實(shí)現(xiàn)token令牌認(rèn)證的示例代碼
1. 什么token
作為計(jì)算機(jī)術(shù)語時,是“令牌”的意思。Token是服務(wù)端生成的一串字符串,以作客戶端進(jìn)行請求的一個令牌,當(dāng)?shù)谝淮蔚卿浐?,服?wù)器生成一個Token便將此Token返回給客戶端,以后客戶端只需帶上這個Token前來請求數(shù)據(jù)即可,無需再次帶上用戶名和密碼。
使用token機(jī)制的身份驗(yàn)證方法,在服務(wù)器端不需要存儲用戶的登錄記錄。
大概的流程:
1 客戶端使用用戶名和密碼請求登錄。
2 服務(wù)端收到請求,驗(yàn)證用戶名和密碼。
3 驗(yàn)證成功后,服務(wù)端會生成一個token,然后把這個token發(fā)送給客戶端。
4 客戶端收到token后把它存儲起來,可以放在cookie或者Local Storage(本地存儲)里。
5 客戶端每次向服務(wù)端發(fā)送請求的時候都需要帶上服務(wù)端發(fā)給的token。
6 服務(wù)端收到請求,然后去驗(yàn)證客戶端請求里面帶著token,如果驗(yàn)證成功,就向客戶端返回請求的數(shù)據(jù)
2. jwt是什么
實(shí)施 Token 驗(yàn)證的方法挺多的,還有一些標(biāo)準(zhǔn)方法,比如 JWT,讀作:jot ,表示:JSON Web Tokens 。JWT 標(biāo)準(zhǔn)的 Token 有三個部分:
1. header(頭部),頭部信息主要包括(參數(shù)的類型--JWT,簽名的算法--HS256)
2. poyload(負(fù)荷),負(fù)荷基本就是自己想要存放的信息(因?yàn)樾畔┞叮粦?yīng)該在載荷里面加入任 何敏感的數(shù)據(jù))
3. sign(簽名),簽名的作用就是為了防止惡意篡改數(shù)據(jù),
例如:中間用點(diǎn)分隔開,并且都會使用 Base64 編碼,所以真正的 Token 看起來像這樣:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.//頭部 eyJpZCI6IjIiLCJleHAiOjE3MDI0NDE3MDcsInVzZXJuYW1lIjoiYWRtaW4ifQ.//負(fù)載 k8F9h5GQB1-rTVi-8hs9jOWnfpJALSk2Y08xeZb7YlE//簽名
3. jwt的依賴坐標(biāo)
springboot引入jwt:
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.1</version> </dependency>
4. springboot整合token
在這里只是給大家一個演示,
首先要知道如何生成一個token 那就是上面講到的請求頭+負(fù)載+簽名
package com.hyh.util; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import com.hyh.domain.User; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Calendar; import java.util.Date; public class JwtUtil { // 生成token // Algorithm.HMAC256():使用HS256生成token,密鑰則是用戶的密碼 private static final String SING = "MusicProject"; public String getToken(User user) { //System.out.println(String.valueOf(user.getId())); // 設(shè)置token過期時間 Calendar instance= Calendar.getInstance(); instance.add(Calendar.DATE,7); String token=""; token= JWT.create() .withClaim("id",String.valueOf(user.getId())) //設(shè)置載荷 .withClaim("username",user.getUsername()) .withExpiresAt(instance.getTime()) //設(shè)置令牌過期的時間 .sign(Algorithm.HMAC256(SING)); return token; } /**, * 驗(yàn)證token 合法性 */ public static DecodedJWT verify(String token) { return JWT.require(Algorithm.HMAC256(SING)).build().verify(token); } }
我把生成token的方法和驗(yàn)證方法弄成了一個工具類
在這里我定義的簽名是靜態(tài)變量 可以根據(jù)自己需求來定義 這里的id是從數(shù)據(jù)庫里面拿到的 這里的驗(yàn)證token符合的話就放行,否則就拋異常。
接下來就需要配置一個攔截器 對于攔截請求資源
public class AuthenticationInterceptor implements HandlerInterceptor { @Autowired UserService userService; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { String token = httpServletRequest.getHeader("token");// 從 http 請求頭中取出 token // 如果不是映射到方法直接通過 if (!(object instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); //檢查有沒有需要用戶權(quán)限的注解 if (method.isAnnotationPresent(TokenRequired.class)) { TokenRequired userLoginToken = method.getAnnotation(TokenRequired.class); if (userLoginToken.required()) { Map<String,Object> map = new HashMap<>(); // 獲取請求頭中令牌 System.out.println(token); try { // 驗(yàn)證令牌 JwtUtil.verify(token); return true; // 放行請求 } catch (SignatureVerificationException e) { e.printStackTrace(); map.put("msg","無效簽名!"); }catch (TokenExpiredException e){ e.printStackTrace(); map.put("msg","token過期"); }catch (AlgorithmMismatchException e){ e.printStackTrace(); map.put("msg","算法不一致"); }catch (Exception e){ e.printStackTrace(); map.put("msg","token無效!"); } map.put("state",false); //設(shè)置狀態(tài) // 將map以json的形式響應(yīng)到前臺 map --> json (jackson) String json = new ObjectMapper().writeValueAsString(map); httpServletResponse.setContentType("application/json;charset=UTF-8"); httpServletResponse.getWriter().println(json); return false; } } return true; }
在這里面 useservice是我自己的實(shí)現(xiàn)類,里面可以查到用戶id
把攔截器注冊:
@Configuration // 配置類 public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthenticationInterceptor()) .addPathPatterns("/api/**") // 攔截所有請求 .excludePathPatterns("/api/login","/api/register"); // 放行登錄 注冊 } }
這里因?yàn)閿r截了所有請求 但是我們可以自己定義一個注解來判斷當(dāng)我們請求資源的時候需不需呀token驗(yàn)證。
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface TokenRequired { boolean required() default true; }
當(dāng)我們在某個資源上加上了這個注解 說明這個資源需要token驗(yàn)證
這是我寫的一個登錄接口 集成了驗(yàn)證碼功能
/* * @description: 用戶的登錄 * @param: User user * @return:result * @author * @date: 2023/9/28 10:03 */ @PostMapping("/login") public Result login(@RequestBody User user,HttpSession httpSession,HttpServletResponse response){ String checkCode = (String) httpSession.getAttribute("checkCode"); System.out.println(checkCode); if (user.getCheCode() == null || !user.getCheCode().equalsIgnoreCase(checkCode)) { return new Result(Code.Err, null, "驗(yàn)證碼錯誤"); } boolean flag = userService.selectUsernamePwd(user); boolean is_exist = userService.selectByName(user.getUsername()); if (flag) { JwtUtil jwtUtil = new JwtUtil(); User user1 = userService.selectByNameToken(user.getUsername()); String token = jwtUtil.getToken(user1); user.setToken(token); Cookie cookie = new Cookie("username",user.getUsername()); cookie.setMaxAge(60*60*24*7); cookie.setPath("/"); response.addCookie(cookie); httpSession.setAttribute("username", user.getUsername()); return new Result(Code.Ok,user,"登錄成功"); }else if(!is_exist){ return new Result(Code.Err,user,"登錄失敗 用戶不存在"); }else{ return new Result(Code.Err,user,"登錄失敗"); } }
這個資源是需要token驗(yàn)證訪問的資源 加了注解 @TokenRequired
@GetMapping @TokenRequired public Result selectAllSingers(HttpServletRequest request) { List<Singer> singers = songService.selectSingerAll(); Integer code = singers != null ? Code.Ok : Code.Err; String msg = singers != null ? "" : "數(shù)據(jù)查詢失敗 請重試"; return new Result(code, singers, msg); }
當(dāng)我們沒有token的時候正常訪問一下:
可以看到是無法訪問的
我們可以先登錄獲取token 在進(jìn)行訪問試一下
因?yàn)槲野裻oken封裝在user里面 所以返回了token 現(xiàn)在把token放到剛剛無法訪問的url的請求頭里,再次訪問一下:
現(xiàn)在訪問有數(shù)據(jù)了 這些數(shù)據(jù)都是我自己封裝好的,你自己可以隨便寫寫字符串返回來進(jìn)行測試。
到此這篇關(guān)于springBoot整合jwt實(shí)現(xiàn)token令牌認(rèn)證的示例代碼的文章就介紹到這了,更多相關(guān)springBoot token令牌認(rèn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot集成?JWT實(shí)現(xiàn)用戶登錄認(rèn)證的項(xiàng)目實(shí)踐
- SpringBoot結(jié)合JWT實(shí)現(xiàn)用戶登錄、注冊、鑒權(quán)
- springboot中通過jwt令牌校驗(yàn)及前端token請求頭進(jìn)行登錄攔截實(shí)戰(zhàn)記錄
- SpringBoot整合JWT(JSON?Web?Token)生成token與驗(yàn)證的流程及示例
- SpringSecurity角色權(quán)限控制(SpringBoot+SpringSecurity+JWT)
- Springboot+jwt實(shí)現(xiàn)在線用戶功能(示例代碼)
相關(guān)文章
Springboot hibernate envers使用過程詳解
這篇文章主要介紹了Springboot hibernate envers使用過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06Swagger異常定位紀(jì)實(shí)Swagger設(shè)計(jì)問題分析
這篇文章主要為大家介紹了Swagger異常定位紀(jì)實(shí)Swagger設(shè)計(jì)的問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-02-02SpringBoot整合mybatis結(jié)合pageHelper插件實(shí)現(xiàn)分頁
在本篇文章里小編給大家整理的是關(guān)于SpringBoot整合mybatis使用pageHelper插件進(jìn)行分頁操作相關(guān)知識點(diǎn),需要的朋友們學(xué)習(xí)下。2020-02-02Simple Java Mail郵件發(fā)送實(shí)現(xiàn)過程解析
這篇文章主要介紹了Simple Java Mail郵件發(fā)送實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11SpringBoot如何處理@KafkaListener消息
Spring通過KafkaMessageListenerContainer、ConcurrentMessageListenerContainer等組件實(shí)現(xiàn)Kafka消息的監(jiān)聽和處理,并通過@KafkaListener注解將業(yè)務(wù)邏輯與Kafka消費(fèi)者連接起來,Spring?Boot自動配置Kafka相關(guān)組件,簡化了Kafka的使用2024-12-12SpringBoot定時任務(wù)動態(tài)擴(kuò)展ScheduledTaskRegistrar詳解
這篇文章主要為大家介紹了SpringBoot定時任務(wù)動態(tài)擴(kuò)展ScheduledTaskRegistrar類示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01關(guān)于Spring MVC框架中攔截器Interceptor的使用解讀
這篇文章主要介紹了關(guān)于Spring MVC框架中攔截器Interceptor的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07Java學(xué)習(xí)之Lambda表達(dá)式的使用詳解
Lambda表達(dá)式是Java SE 8中一個重要的新特性,允許通過表達(dá)式來代替功能接口。本文將通過一些簡單的示例和大家講講Lamda表達(dá)式的使用,感興趣的可以了解一下2022-12-12