物流項目第二期(用戶端登錄與雙token三驗證)

第一期內容:

物流項目第一期(登錄業務)-CSDN博客

用戶端登錄

實現分析

登錄功能?

@Data
public class UserLoginRequestVO {@ApiModelProperty("登錄臨時憑證")private String code;@ApiModelProperty("手機號臨時憑證")private String phoneCode;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO {@ApiModelProperty("微信唯一標識符")private String openid;@ApiModelProperty("短令牌,有效期較短")private String accessToken;@ApiModelProperty("長令牌,有效期較長")private String refreshToken;@ApiModelProperty("是否綁定手機號 0否 1是")private Integer binding;}

小程序登錄

    @Value("${sl.wechat.appid}")private String appid;@Value("${sl.wechat.secret}")private String secret;public static final String LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";private static final int TIMEOUT = 20000;@Overridepublic JSONObject getOpenid(String code) throws IOException {//文檔:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html//1. 封裝參數Map<String, Object> requestParam = MapUtil.<String, Object>builder().put("appid", this.appid) //小程序 appId.put("secret", this.secret) //小程序 appSecret.put("js_code", code) //	登錄時獲取的 code,可通過wx.login獲取.put("grant_type", "authorization_code") //授權類型.build();//2. 發送get請求HttpResponse response = HttpRequest.get(LOGIN_URL) //設置get請求url.form(requestParam) //設置表單參數.timeout(TIMEOUT) //設置超時時間,20s.execute();//執行請求if (response.isOk()) {// 3. 解析響應的結果,如果出現錯誤拋出異常JSONObject jsonObject = JSONUtil.parseObj(response.body());if (jsonObject.containsKey("errcode")) {throw new SLWebException(jsonObject.toString());}return jsonObject;}String errMsg = StrUtil.format("調用微信登錄接口出錯! code = {}", code);throw new SLWebException(errMsg);}

獲取手機號

    public static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";public static final String PHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";@Overridepublic String getPhone(String code) throws IOException {//接口文檔:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html//1. 獲取手機號,需要先獲取微信access_tokenString accessToken = this.getToken();//2. 封裝參數Map<String, Object> requestParam = MapUtil.<String, Object>builder().put("code", code) //手機號獲取憑證.build();//3. 發送post請求HttpResponse response = HttpRequest.post(PHONE_URL + accessToken) //設置post請求url.body(JSONUtil.toJsonStr(requestParam)) //設置請求體參數.timeout(TIMEOUT) //設置超時時間,20s.execute();//執行請求if (response.isOk()) {// 4. 解析響應的結果,如果errcode不等于0拋出異常JSONObject jsonObject = JSONUtil.parseObj(response.body());if (ObjectUtil.notEqual(jsonObject.getInt("errcode"), 0)) {throw new SLWebException(jsonObject.toString());}return jsonObject.getByPath("phone_info.purePhoneNumber", String.class);}String errMsg = StrUtil.format("調用獲取手機號接口出錯!");throw new SLWebException(errMsg);}private String getToken() {//接口文檔:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html//1. 封裝參數Map<String, Object> requestParam = MapUtil.<String, Object>builder().put("appid", this.appid) //小程序 appId.put("secret", this.secret) //小程序 appSecret.put("grant_type", "client_credential") //授權類型.build();//2. 發送get請求HttpResponse response = HttpRequest.get(TOKEN_URL) //設置get請求url.form(requestParam) //設置表單參數.timeout(TIMEOUT) //設置超時時間,20s.execute();//執行請求if (response.isOk()) {// 3. 解析響應的結果,如果出現錯誤拋出異常JSONObject jsonObject = JSONUtil.parseObj(response.body());if (jsonObject.containsKey("errcode")) {throw new SLWebException(jsonObject.toString());}//TODO 緩存token到redis,不應該每次都獲取tokenreturn jsonObject.getStr("access_token");}String errMsg = StrUtil.format("調用獲取接口調用憑據接口出錯!");throw new SLWebException(errMsg);}

實現登錄

    /*** 登錄** @param userLoginRequestVO 登錄code* @return 用戶信息*/@Overridepublic UserLoginVO login(UserLoginRequestVO userLoginRequestVO) throws IOException {//1. 調用微信開發平臺的接口,根據臨時登錄code獲取openid等信息JSONObject jsonObject = this.wechatService.getOpenid(userLoginRequestVO.getCode());String openid = jsonObject.getStr("openid");//2. 根據openid來確認是否為新用戶,新用戶進行注冊,老用戶無需直接注冊MemberDTO memberDTO = this.getByOpenid(openid);if (ObjectUtil.isEmpty(memberDTO)) {//新用戶MemberDTO newMember = MemberDTO.builder().openId(openid) //設置openid.authId(jsonObject.getStr("unionid")) //設置平臺唯一id,若當前小程序已綁定到微信開放平臺帳號下會返回.build();//注冊用戶this.save(newMember);//再次查詢用戶信息memberDTO = this.getByOpenid(openid);}//3. 調用微信開發平臺的接口,獲取用戶手機號,如果用戶手機號有更新,需要進行更新操作String phone = this.wechatService.getPhone(userLoginRequestVO.getPhoneCode());if (ObjectUtil.notEqual(phone, memberDTO.getPhone())) {//更新手機號memberDTO.setPhone(phone);this.memberFeign.update(memberDTO.getId(), memberDTO);}//4. 生成token,將用戶id存儲到token中Map<String, Object> claims = MapUtil.<String, Object>builder().put(Constants.GATEWAY.USER_ID, memberDTO.getId()) //將id存入token.build();String accessToken = this.tokenService.createAccessToken(claims);//5. 返回封裝響應數據return UserLoginVO.builder().openid(openid).accessToken(accessToken).binding(StatusEnum.NORMAL.getCode()).build();}
    public String createAccessToken(Map<String, Object> claims) {//生成短令牌的有效期時間單位為:分鐘return JwtUtils.createToken(claims, jwtProperties.getPrivateKey(), jwtProperties.getAccessTtl(),DateField.MINUTE);}

登錄流程總結

+-----------------------+
|      小程序端         |
+-----------------------+|v
+-----------------------+
|   調用微信登錄接口     |
|   獲取 openid         |
+-----------------------+|v
+-----------------------+
| 根據 openid 注冊/查詢用戶 |
+-----------------------+|v
+-----------------------+
| 調用微信獲取手機號接口   |
| 需要先獲取 access_token |
+-----------------------+|v
+-----------------------+
| 更新用戶手機號(如有變化)|
+-----------------------+|v
+-----------------------+
| 生成你自己的 JWT Token   |
| 返回給前端              |
+-----------------------+

雙token三驗證

單token存在的問題

在司機端、快遞員端和管理管,登錄成功后會生成jwt的token,前端將此token保存起來,當請求后端服務時,在請求頭中攜帶此token,服務端需要對token進行校驗以及鑒權操作,這種模式就是【單token模式】。

該模式存在什么問題嗎?

其實是有問題的,主要是token有效期設置長短的問題,如果設置的比較短,用戶會頻繁的登錄,如果設置的比較長,會不太安全,因為token一旦被黑客截取的話,就可以通過此token與服務端進行交互了。

另外一方面,token是無狀態的,也就是說,服務端一旦頒發了token就無法讓其失效(除非過了有效期),這樣的話,如果我們檢測到token異常也無法使其失效,所以這也是無狀態token存在的問題。

為了解決此問題,我們將采用【雙token三驗證】的解決方案來解決此問題。

方案原理?

代碼實現?

生成刷新token

    public static final String REDIS_REFRESH_TOKEN_PREFIX = "SL_CUSTOMER_REFRESH_TOKEN_";@Overridepublic String createRefreshToken(Map<String, Object> claims) {//生成長令牌的有效期時間單位為:小時Integer ttl = jwtProperties.getRefreshTtl();String refreshToken = JwtUtils.createToken(claims, jwtProperties.getPrivateKey(), ttl);//長令牌只能使用一次,需要將其存儲到redis中,變成有狀態的String redisKey = this.getRedisRefreshToken(refreshToken);this.stringRedisTemplate.opsForValue().set(redisKey, refreshToken, Duration.ofHours(ttl));return refreshToken;}private String getRedisRefreshToken(String refreshToken) {//md5是為了縮短key的長度return REDIS_REFRESH_TOKEN_PREFIX + SecureUtil.md5(refreshToken);}

刷新token

刷新token的動作是在refresh_token過期之后進行的,主要實現關鍵點有:

  • 校驗refresh_token是否被偽造以及是否在有效期內
  • 從redis中查詢,是否不存在,如果不存在說明已經失效或已經使用過,如果存在,就需要將其刪除
  • 重新生成一對token,響應結果
    @Overridepublic UserLoginVO refreshToken(String refreshToken) {if (StrUtil.isEmpty(refreshToken)) {return null;}Map<String, Object> originClaims = JwtUtils.checkToken(refreshToken, this.jwtProperties.getPublicKey());if (ObjectUtil.isEmpty(originClaims)) {//token無效return null;}//通過redis校驗,原token是否使用過,來確保token只能使用一次String redisKey = this.getRedisRefreshToken(refreshToken);Boolean bool = this.stringRedisTemplate.hasKey(redisKey);if (ObjectUtil.notEqual(bool, Boolean.TRUE)) {//原token過期或已經使用過return null;}//刪除原tokenthis.stringRedisTemplate.delete(redisKey);//重新生成長短令牌String newRefreshToken = this.createRefreshToken(originClaims);String accessToken = this.createAccessToken(originClaims);return UserLoginVO.builder().accessToken(accessToken).refreshToken(newRefreshToken).build();}
    /*** 刷新token,校驗請求頭中的長令牌,生成新的長短令牌** @param refreshToken 原令牌* @return 登錄結果*/@PostMapping("/refresh")@ApiOperation("刷新token")public R<UserLoginVO> refresh(@RequestHeader(Constants.GATEWAY.REFRESH_TOKEN) String refreshToken) {return R.success(this.memberService.refresh(refreshToken));}
    @Overridepublic UserLoginVO refresh(String refreshToken) {return this.tokenService.refreshToken(refreshToken);}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81682.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81682.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81682.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

精準掌控張力動態,重構卷對卷工藝設計

一、MapleSim Web Handling Library仿真和虛擬調試解決方案 在柔性材料加工領域&#xff0c;卷對卷&#xff08;Roll-to-Roll&#xff09;工藝的效率與質量直接決定了產品競爭力。如何在高動態生產場景中實現張力穩定、減少斷裂風險、優化加工速度&#xff0c;是行業長期面臨的…

Voxblox算法

文章目錄 1. 算法簡介2. 由 TSDF 構建 ESDF 的方法2.1. 論文解讀2.2. 偽代碼實現 1. 算法簡介 Voxblox 算法出現于文獻《Voxblox: Incremental 3D Euclidean Signed Distance Fields for On-Board MAV Planning》&#xff0c;PDF 鏈接&#xff1a;https://arxiv.org/pdf/1611.…

計算機圖形學基礎--Games101筆記(一)數學基礎與光柵化

文章目錄 數學基礎向量插值三角形插值雙線性插值 平面定義法線-點表示 第一部分&#xff1a;光柵化坐標變換二維變換3D變換視圖變換&#xff08;MVP&#xff09;投影變換 光柵化采樣抗鋸齒&#xff08;反走樣&#xff09;可見性&#xff08;遮擋&#xff09; 著色與紋理Blinn-P…

@RequestParam 和 @RequestBody、HttpServletrequest 與HttpServletResponse

在Java Web開發中&#xff0c;RequestParam、RequestBody、HttpServletRequest 和 HttpServletResponse 是常用的組件&#xff0c;它們用于處理HTTP請求和響應。下面分別介紹它們的使用場景和使用方法&#xff1a; 1. RequestParam RequestParam 是Spring MVC框架中的注解&am…

【硬核數學】2. AI如何“學習”?微積分揭秘模型優化的奧秘《從零構建機器學習、深度學習到LLM的數學認知》

在上一篇中&#xff0c;我們探索了線性代數如何幫助AI表示數據&#xff08;向量、矩陣&#xff09;和變換數據&#xff08;矩陣乘法&#xff09;。但AI的魅力遠不止于此&#xff0c;它最核心的能力是“學習”——從數據中自動調整自身&#xff0c;以做出越來越準確的預測或決策…

10.15 LangChain v0.3重磅升級:Tool Calling技術顛覆大模型工具調用,效率飆升300%!

LangChain v0.3 技術生態與未來發展:支持 Tool Calling 的大模型 關鍵詞:LangChain Tool Calling, 大模型工具調用, @tool 裝飾器, ToolMessage 管理, Few-shot Prompting 1. Tool Calling 的技術革新 LangChain v0.3 的工具調用(Tool Calling)功能標志著大模型應用開發進…

[架構之美]從PDMan一鍵生成數據庫設計文檔:Word導出全流程詳解(二十)

[架構之美]從PDMan一鍵生成數據庫設計文檔&#xff1a;Word導出全流程詳解&#xff08;二十&#xff09; 一、痛點 你是否經歷過這些場景&#xff1f; 數據庫字段頻繁變更&#xff0c;維護文檔耗時費力用Excel維護表結構&#xff0c;版本混亂難以追溯手動編寫Word文檔&#…

Image and depth from a conventional camera with a coded aperture論文閱讀

Image and depth from a conventional camera with a coded aperture 1. 研究目標與實際意義1.1 研究目標1.2 實際問題與產業意義2. 創新方法:編碼光圈設計與統計模型2.1 核心思路2.2 關鍵公式與模型架構2.2.1 圖像形成模型2.2.2 深度可區分性準則2.2.3 統計模型與優化框架2.2…

JMeter 教程:使用 HTTP 請求的參數列表發送 POST 請求(form 表單格式)

目錄 ? 教程目的 &#x1f6e0;? 準備工作 &#x1f4c4; 操作步驟 第一步&#xff1a;新建測試計劃 第二步&#xff1a;添加 HTTP 請求 第三步&#xff1a;添加參數列表&#xff08;表單參數&#xff09; 第四步&#xff1a;添加結果查看器 第五步&#xff1a;運行測…

交易所開發:構建功能完備的金融基礎設施全流程指南

交易所開發&#xff1a;構建功能完備的金融基礎設施全流程指南 ——從技術架構到合規安全的系統性解決方案 一、開發流程&#xff1a;從需求分析到運維優化 開發一款功能完備的交易所需要遵循全生命周期管理理念&#xff0c;涵蓋市場定位、技術實現、安全防護和持續迭代四大階…

【數據結構篇】排序1(插入排序與選擇排序)

注&#xff1a;本文以排升序為例 常見的排序算法&#xff1a; 目錄&#xff1a; 一 直接插入排序&#xff1a; 1.1 基本思想&#xff1a; 1.2 代碼&#xff1a; 1.3 復雜度&#xff1a; 二 希爾排序&#xff08;直接插入排序的優化&#xff09;&#xff1a; 2.1 基本思想…

Cursor日常配置指南

文章目錄 整體說明一、簡單介紹1.1、簡介1.2、功能 二、日常配置2.1、Profiles 簡介2.2、Cursor 配置2.2.1、通用設置&#xff08;General&#xff09;2.2.2、功能設置&#xff08;Features&#xff09;2.2.2.1、長上下文&#xff08;Large context&#xff09;2.2.2.2、代碼索…

客戶體驗數據使用的三種視角——旅程視角

企業收集到大量的客戶體驗數據之后&#xff0c;應該如何應用&#xff1f;有哪些主要的使用場景和分析視角呢&#xff1f;接下來&#xff0c;體驗家團隊將通過三篇文章陸續介紹體驗數據的三種應用場景&#xff0c;以幫助企業更有效地利用體驗數據進行改進。 這三個場景分別是…

大語言模型怎么進行記憶的

大語言模型怎么進行記憶的 大語言模型(LLM)本身是無狀態的,每次輸入獨立處理,但可通過以下方式實現對話記憶及長期記憶能力: 模型架構改進 顯式記憶模塊: 記憶網絡(Memory Networks) :在模型里嵌入可讀寫的記憶單元,像鍵值存儲 (Key - Value Memory)或動態記憶矩…

Spring Boot 與 RabbitMQ 的深度集成實踐(三)

高級特性實現 消息持久化 在實際的生產環境中&#xff0c;消息的可靠性是至關重要的。消息持久化是確保 RabbitMQ 在發生故障或重啟后&#xff0c;消息不會丟失的關鍵機制。它涉及到消息、隊列和交換機的持久化配置。 首先&#xff0c;配置隊列持久化。在創建隊列時&#xf…

成功案例丨GEZE與Altair合作推動智能建筑系統開發

Altair 作為計算智能領域的全球領導者&#xff0c;將分別在北京、上海、成都、深圳舉辦 “AI驅動&#xff0c;仿真未來”Altair 區域技術交流會。屆時將匯聚行業專家與先鋒企業&#xff0c;共同探討仿真智能化如何賦能工業創新&#xff0c;分享最新仿真與 AI 技術的應用實踐。歡…

DDoS與CC攻擊:誰才是服務器的終極威脅?

在網絡安全領域&#xff0c;DDoS&#xff08;分布式拒絕服務&#xff09;與CC&#xff08;Challenge Collapsar&#xff09;攻擊是兩種最常見的拒絕服務攻擊方式。它們的目標都是通過消耗服務器資源&#xff0c;導致服務不可用&#xff0c;但攻擊方式、威脅程度和防御策略存在顯…

循環中使用el-form

循環中使用el-form 主要是校驗問題 el-table 的數據 :data“ruleForm.tableData” :prop“‘tableData.’ $index ‘.name’” :rules“rules.name” <el-button type"primary" click"addNewData">新增項目</el-button><el-form :model&…

SAP學習筆記 - 開發13 - CAP 之 添加數據庫支持(Sqlite)

上一章學習了CAP開發準備&#xff0c;添加Service。 SAP學習筆記 - 開發12 - CAP 之 開發準備&#xff0c;添加服務-CSDN博客 本章繼續學習CAP開發 - 添加數據庫支持&#xff08;Sqlite&#xff09;。 目錄 1&#xff0c;數據庫準備 - H2 內存數據庫 - Sqlite數據庫 a&…

【數據結構與算法】——圖(三)——最小生成樹

前言 本將介紹最小生成樹以及普里姆算法&#xff08;Prim&#xff09;和克魯斯卡爾&#xff08;Kruskal&#xff09; 本人其他博客&#xff1a;https://blog.csdn.net/2401_86940607 圖的基本概念和存儲結構&#xff1a;【數據結構與算法】——圖&#xff08;一&#xff09; 源…