Redis實戰-基于Session實現分布式登錄

1.流程分析

1.1發送短信驗證碼

????????提交手機號的時候要進行校驗手機號,校驗成功才會去生成驗證碼,將驗證碼保存到session,發生他把這部分那。

1.2短信驗證碼登錄/注冊

????????如果提交手機號和驗證碼之后,校驗一致才進行根據手機號查詢用戶,進行創建新用戶/登錄成功,將信息保存到session進行返回。

1.3校驗登錄狀態

????????前端傳遞過來cookie,攜帶其中的sessionID,從session中獲取到用戶信息,校驗是否存在,存在就將用戶保存到TheadLocal,不存在就攔截。

2.實現發送短信驗證碼

????????利用一些封裝的工具生成驗證碼和校驗手機號。

????????利用session進行存儲驗證碼,方便校驗,比較不錯。

@Override
public Result sendCode(String phone, HttpSession session) {if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手機號格式錯誤!");}// 3. 生成驗證碼String code = RandomUtil.randomNumbers(6);// 2. 保存驗證碼到sessionsession.setAttribute(LOGIN_CODE_KEY + phone, code);// 發送驗證碼// todo 實現發送驗證碼log.debug("發送短信驗證碼成功: {}", code);// 返回OKreturn Result.ok();
}

3.短信驗證碼登錄

????????最重要的可能是封裝的思想吧,封裝一定的常量和借助mybatis-plus的高級的功能。

????????重點:抽取邏輯,mybatis-plus高級功能。

/*** 實現登錄功能** @param loginForm* @param session* @return*/
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {// 1. 校驗手機號String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手機號格式不正確");}// 2. 校驗驗證碼// 從session中獲取到驗證碼String phoneForCode = (String) session.getAttribute(LOGIN_CODE_KEY + phone);// 校驗驗證碼String code = loginForm.getCode();if (!StrUtil.equals(code, phoneForCode)) {return Result.fail("驗證碼錯誤!");}// 3. 查詢用戶是否存在// QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();// userQueryWrapper.eq("phone", phone);// User user = userMapper.selectOne(userQueryWrapper);User user = query().eq("phone", phone).one();// 不存在則注冊if (user == null) {user = createUserWithPhone(phone);}// 4. 保存信息到session中session.setAttribute("user", user);return Result.ok();
}/*** 封裝一個創建用戶的邏輯** @param phone* @return*/
private User createUserWithPhone(String phone) {// 1. 創建用戶User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));// 2. 保存用戶save(user);return user;
}

4.登錄驗證功能

????????需要封裝一個登錄校驗的功能供前端進行調取使用。

????????重點:通過SpringMVC進行攔截請求,封裝數據到ThreadLocal中。

????????使用SpringMVC統一攔截請求可以方便將數據存儲到ThreadLocal中,這樣就無需在每個接口中進行配置了十分方便。

????????ThreadLocal是每個tomcat創建的請求線程中獨有的,不會被其它線程訪問到的。

????????封裝攔截器,從session中獲取到數據即可,最后一定要在請求后攔截器中將ThreadLocal中的數據刪除。

package com.hmdp.utils;import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import org.springframework.beans.BeanUtils;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;/*** 登錄攔截器*/
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 獲取sessonHttpSession session = request.getSession();// 2. 獲取用戶信息User user = (User) session.getAttribute("user");// 3. 處理用戶不存在if (user == null) {response.setStatus(401);return false;}// 4. 存儲數據到ThreadLocalUserDTO userDTO = new UserDTO();BeanUtils.copyProperties(user, userDTO);UserHolder.saveUser(userDTO);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}

????????將攔截器配置到SpringBoot中,可以進行配置什么路徑需要排除,什么無需排除。

/*** MVC攔截器配置*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/voucher/**","/blog/hot","/user/code","/user/login").order(1);}
}

????????通過攔截器可以完成登錄校驗功能。

5.集群session共享問題

????????多臺tomcat是并不進行共享session存儲空間的,雖然tomact提供了將session共享到多臺tomcat,但是這樣性能太差了,還會有很多問題,所以為了進行實現分布式服務器tomcat共享session,建議使用redis進行替代session,這樣就可以做到分布式session了。

6.基于redis實現

????????其實就是使用redis去利用鍵值對的形式進行存儲用戶的信息.

????????key的設計:項目名:業務名:類型:id。

????????這里只使用項目名,類型和id,使用這種結構式key可以大大的幫助到我們。

????????其實就是把使用session的部分換為使用redis了,存儲key-value,取出key-value都使用redis即可。

????????重要點:使用token令牌替代了cookie。

????????使用redis的時候,一定要使用token嗎?未必的,只是說將token作為一個幫助客戶端和服務端之間進行身份認證的手段,完全也可以進行使用分布式session,使用redis存儲session,前端依然攜帶cookie而來,所以這只是一種手段而已,各有所長。

6.1獲取驗證碼

????????session => redis。

????????沒有太多亮點,就簡單分析一下key的構造吧。

????????login:code:phone,典型的業務+類型+分辨標識key,這樣就很好的能架構出合理的key了。

public Result sendCode(String phone, HttpSession session) {if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手機號格式錯誤!");}// 1. 生成驗證碼String code = RandomUtil.randomNumbers(6);// 2. 保存驗證碼到session// session.setAttribute(LOGIN_CODE_KEY, code);// 2. 保存驗證碼到redis// 構造keyString key = LOGIN_CODE_KEY + phone;stringRedisTemplate.opsForValue().set(key, code, LOGIN_CODE_TTL, TimeUnit.SECONDS);// 3. 發送驗證碼// todo 實現發送驗證碼log.debug("發送短信驗證碼成功: {}", code);// 返回OKreturn Result.ok();
}

6.2注冊/登錄

public Result login(LoginFormDTO loginForm, HttpSession session) {// 1. 校驗手機號String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手機號格式不正確");}// 2. 校驗驗證碼// 從session中獲取到驗證碼// String phoneForCode = (String) session.getAttribute(LOGIN_CODE_KEY);// 從redis中獲取到驗證碼String codeKey = LOGIN_CODE_KEY + phone;String phoneForCode = stringRedisTemplate.opsForValue().get(codeKey);// 校驗驗證碼String code = loginForm.getCode();if (!StrUtil.equals(code, phoneForCode)) {return Result.fail("驗證碼錯誤!");}// 3. 查詢用戶是否存在// QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();// userQueryWrapper.eq("phone", phone);// User user = userMapper.selectOne(userQueryWrapper);User user = query().eq("phone", phone).one();// 不存在則注冊if (user == null) {user = createUserWithPhone(phone);}// 4. 保存信息到session中// session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));// 4. 保存信息到redis中// 4.1 隨機生成token, 作為登錄令牌String token = UUID.randomUUID().toString();// 4.2 將User對象轉換為Hash存儲UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));// 4.3 存儲數據到redisString userKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(userKey, userMap);// 4.4 設置stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);return Result.ok(token);
}

6.2.1key的設計

????????其中key的設計是login:token:token,也是遵循的業務+類型+標識的設計思想。

6.2.2token的設計

????????這里的token很值得分析一下,尤其是可以對比蒼穹外賣的進行分析。

????????這里的key僅僅是用來作為一個獲取redis中數據使用的,并不是加密攜帶payload負載數據的,

????????其實就是使用了token替代了sessionID,但是其實還是有一個更為方便的解決方法的。

6.2.3存儲hash數據到redis中的注意事項

1.使用什么API?

????????使用的是StringRedisTemplate中的opsForHash.putAll(),這個方法接收兩個參數,key => 字符串,value => Map,它可以將Map中的key-value全部存入redis的hash中,十分方便。

2.Map中數據規范是什么樣的?

????????由于我們進行使用的Redis客戶端是stringRedisTemplate,這就限制了我們存儲hash數據的時候,map中的key-value都必須是string類型的,如果出現了其它類型:比如Long類型,就會拋出錯誤,所以我們在將DTO轉換為Map的時候,必須對value進行處理。

????????借助hutool工具類中的BeanUtil.beanToMap就可以完成這個操作。

UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),CopyOptions.create().setFieldValueEditor((filedName, filedValue) -> filedValue.toString()));

6.2.4Redis中Key設置時間的問題

????????使用stringRedisTemplate中的expire進行設置key的存活時間,傳入key,time,TimeUnit即可。

6.3登錄認證攔截器

????????我們需要將以前使用session進行將數據取出存入ThreadLocal的邏輯變更為使用前端傳遞來的token進行獲取數據,從redis中獲取用戶數據DTO進行使用。

6.3.1思考:沒有被SpringIOC托管的對象如何注入Bean

????????鑒于我們的登錄攔截器配置類是我們自定義的,并且沒有托管到SpringIOC容器,所以我們不能使用Resouce/Autowired。

????????那應該怎么辦呢?我們發現MVC攔截器配置類是使用@Configuration進行注解的,這個類會被托管到SpringIOC容器,而且我們的自定義攔截器也被該類實例化了一個對象,所以完全可以通過該類將Bean在自定義攔截器實例化的時候,傳遞進來。

????????在構造函數被回調的時候,接收StringRedisTemplate對象,進行賦值給自身字段。

/*** 登錄攔截器*/
public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}}

????????將StringRedisTemplate進行注入到MVC配置類中,在調用攔截器構造函數的時候進行注入StringRedisTemplate進去即可。

/*** MVC攔截器配置*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/voucher/**","/blog/hot","/user/code","/user/login").order(1);}
}

6.3.2整體登錄校驗流程

/*** 登錄攔截器*/
public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 獲取sesson// HttpSession session = request.getSession();// 1. 獲取請求頭中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {response.setStatus(401);return false;}// 2. 獲取用戶信息// UserDTO user = (UserDTO) session.getAttribute("user");String userKey = LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(userKey);// 3. 處理用戶不存在if (userMap.isEmpty()) {response.setStatus(401);return false;}// 4. 將用戶數據map -> userDTOUserDTO user = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 5. 存儲數據到ThreadLocalUserHolder.saveUser(user);// 6. 刷新token有效期stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 7. 放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}

????????1.整體流程:先從request請求頭中獲取到authorization中的token數據 => 然后封裝一個userkey => 然后封裝出來一個KEY,在redis客戶端中獲取到userDTO數據 => 然后處理用戶不存在(redis獲取不到數據的情況)=> 將用戶數據map轉換為userDTO => 將用戶DTO存儲到ThreadLocal中即可 => 最后進行刷新token有效期。

6.3.3刷新token時間

????????token在一定時間后會過期,但是如果在用戶持續使用的過程中過期,那真是一個糟糕的事件,所以要采用攔截器刷新token時間的方式,這樣就是在用戶持續使用的時候,可以幫用戶進行刷新token,延期,不會導致用戶持續使用的時候過期。

????????在攔截器中進行token續期,是一個非常聰明的決策,這里采用的續期策略是,只要發送了請求,在token有效期內還在使用,就將時間續期到原始狀態。

// 6. 刷新token有效期
stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);

7.登錄攔截器和刷新緩存攔截器如何設置?

7.1拆分的思路

????????主要是因為有一些請求是打不到登錄攔截器的,一些不需要登錄的請求根本打不到登錄校驗攔截器,所以我們不能將刷新token時間放在登錄攔截器中去做,因為這樣就會有可能用戶看的都是不需要登錄的接口數據,這樣就會導致token無法進行續期,有可能出現用戶看著看著就token過期了,所以為了避免這種情況的發生,可以進行設置兩個攔截器:1.刷新token攔截器,所有接口都可以進行刷新token請求,當用戶進行發送請求之后,從redis中獲取到用戶的token數據(因為前端會將token傳遞過來),當查詢到數據的時候,就會去更新token時間,如果有數據將數據存儲到ThreadLocal中。2.登錄狀態攔截器,僅僅進行攔截需要登錄狀態才能進行訪問的接口,在攔截器中進行看一下ThreadLocal是否有數據,有就放行,無則滾蛋。

7.2實現token刷新攔截器

/*** 更新攔截器*/
public class RefreshInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 獲取sesson// HttpSession session = request.getSession();// 1. 獲取請求頭中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {return true;}// 2. 獲取用戶信息// UserDTO user = (UserDTO) session.getAttribute("user");String userKey = LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(userKey);// 3. 處理用戶不存在if (userMap.isEmpty()) {return true;}// 4. 將用戶數據map -> userDTOUserDTO user = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 5. 存儲數據到ThreadLocalUserHolder.saveUser(user);// 6. 刷新token有效期stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 7. 放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}

7.3實現登錄攔截器

/*** 登錄攔截器*/
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (UserHolder.getUser() == null) {response.setStatus(401);return false;}return true;}}

7.4SpringMVC配置攔截器

????????攔截器的優先級:可以在registry注冊的時候進行指定order,里面的排序數越小的越先執行,如果不進行指定優先級,就按代碼的順序進行注冊,越靠上進行注冊的攔截器,越先執行。

????????這里將刷新token的攔截器放在最前面,指定的順序數是最小的。

/*** MVC攔截器配置*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/voucher/**","/blog/hot","/user/code","/user/login").order(1);registry.addInterceptor(new RefreshInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);}
}

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

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

相關文章

瘋狂星期四文案網第47天運營日記

網站運營第47天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 今日訪問量 今日搜索引擎收錄情況 必應現在是邊收錄邊k頁面 百度快倒閉 網站優化點 完善工作流&#xff0c;全面實現文案自動化采集&#xff0c;se…

Vue生命周期以及自定義鉤子和路由

Vue生命周期常用的onMounted掛載后執行和onUnmounted卸載前以及onupdated更新后實際上用react對比就是useEffect&#xff0c;而且掛載順序也是子組件先于父組件然后往外的棧結構&#xff0c;先進后出。1.Vue的生命周期<template><h2>當前求和為{{ sum }}</h2>…

探索Thompson Shell:Unix初代Shell的智慧

引言 在計算機科學的漫漫長河中&#xff0c;Thompson Shell 無疑占據著舉足輕重的開創性地位&#xff0c;它是 Unix 系統的第一個 shell&#xff0c;誕生于 1971 年&#xff0c;由計算機領域的傳奇人物 Ken Thompson 開發。在那個計算機技術剛剛起步、硬件資源極度匱乏的年代&a…

MySQL B+ 樹索引詳解:從原理到實戰優化

引言在現代數據庫應用中&#xff0c;查詢效率是影響系統性能的關鍵因素之一。而索引&#xff0c;尤其是 B 樹索引&#xff0c;是 MySQL 中最常用、最重要的性能優化手段。正確使用索引可以將查詢時間從毫秒級降低到微秒級&#xff0c;極大地提升應用響應速度。1. B 樹索引的重要…

計算機內存中的整型存儲奧秘、大小端字節序及其判斷方法

目錄 一、回顧與引入&#xff1a;整數在內存中的存儲方式 為什么要采用補碼存儲&#xff1f; 二、大小端字節序及其判斷方法 1、什么是大小端&#xff1f; 2、為什么存在大小端&#xff1f; 3、練習 練習1&#xff1a;簡述大小端概念并設計判斷程序&#xff08;百度面試…

Redis 最常用的 5 種數據類型

Redis 支持多種靈活的數據類型&#xff0c;每種類型針對特定場景優化。以下是 **Redis 最常用的 5 種數據類型**及其核心特點和應用場景&#xff1a;1. 字符串&#xff08;String&#xff09;描述&#xff1a;最基本的數據類型&#xff0c;可存儲文本、數字&#xff08;整數/浮…

【嵌入式】RK3588 對比 NVIDIA Jetson,Radxa Rock 5B vs Orange Pi 5 Max

RK3588這個芯片,適合AI應用么,為什么這么貴呢 AI 邊緣盒子里的旗艦芯 深度分析一下 RK3588(瑞芯微 Rockchip RK3588) 為什么被很多人關注在 AI 應用,以及它價格偏高的原因。 ?? 1. RK3588 的基本情況 制程:8nm(Samsung 8nm LP) CPU:8 核 big.LITTLE 架構(4 Cortex-…

暴雨讓高性能計算更“冷靜”

當AI大模型的參數突破萬億&#xff0c;當深地探測的精度邁向微米&#xff0c;當數字經濟的脈搏與千行百業深度共振&#xff0c;算力已成為驅動時代向前的核心引擎。然而&#xff0c;傳統風冷技術在高密度算力需求面前漸顯乏力——機柜內的熱浪如同無形的枷鎖&#xff0c;既制約…

SpringAI集成MCP

文章目錄1_調用公用MCP2_Stdio方式3_Stdio實現原理4_SSE方式5_自定義MCP客戶端6_MCP Server權限控制SpringAI 通過 SpringBoot 集成擴展了 MCP Java SDK &#xff0c;提供了客戶端和服務端 starter&#xff0c;讓 AI 應用程序快速支持 MCP。接下來直接演示。 1_調用公用MCP 在…

Spring Start Here 讀書筆記:第10章 Implementing REST services

REST 服務可用于實現兩個應用之間的通訊&#xff0c;包括 Web 應用中的客戶端和服務器之間&#xff0c;移動應用與后端服務之間&#xff0c;或兩個后端服務之間。 10.1 使用 REST 服務在應用之間交換數據 REST端點是應用程序通過 Web 協議公開服務的方式&#xff0c;因此也稱…

SYBASE ASE、Oracle、MySQL/MariaDB、SQL Server及PostgreSQL在郵件/短信發送功能上的全面橫向對比報告

以下是對SYBASE ASE、Oracle、MySQL/MariaDB、SQL Server及PostgreSQL在郵件/短信發送功能上的全面橫向對比報告&#xff08;截至2025年8月最新版本&#xff09;&#xff0c;涵蓋技術實現、配置復雜度、適用場景及權威評測&#xff1a;??一、郵件發送能力對比????1. Orac…

服務器與客戶端

目錄 一、服務器&#xff08;Server&#xff09; 核心特點 常見類型 二、客戶端&#xff08;Client&#xff09; 核心特點 常見類型 客戶端與服務器的交互流程 補充&#xff1a;與 “對等網絡&#xff08;P2P&#xff09;” 的區別 C/S模式 一、C/S 模式的核心原理 …

GaussDB 并發自治事務數達到最大值處理案例

1 業務背景自治事務&#xff08;Autonomous Transactions&#xff09;是一種高級特性&#xff0c;允許你在一個事務中執行另一個獨立的事務。這種機制特別有用&#xff0c;尤其是在需要在一個事務中執行多個操作但又不想因為其中一個操作失敗而影響整個事務的場景。2 業務影響在…

【傳奇開心果系列】Flet分頁自定義組件CustomPaginationComponent封裝版自定義模板

Flet分頁自定義組件CustomPaginationComponent封裝版自定義模板一、效果展示GIF動圖二、應用場景三、特色說明四、源碼下載地址一、效果展示GIF動圖 二、應用場景 圖片瀏覽應用&#xff1a; 用戶可以通過分頁組件瀏覽多張圖片&#xff0c;每點擊一次“上一頁”或“下一頁”按鈕…

數據安全——39頁解讀數字化轉型大數據安全基礎培訓方案【附全文閱讀】

適應人群為企業數據安全管理人員、IT 運維人員、數字化轉型決策者、網絡安全工程師及關注大數據安全的從業人員。主要內容圍繞數字化轉型中大數據安全展開,核心包括基礎概念(信息、數據與大數據的定義及區別,大數據 4V 特點與來源);安全風險(企業面臨的數據資產管理缺失、…

week3-[二維數組]小方塊

week3-[二維數組]小方塊 題目描述 如果四個數 a,b,c,da,b,c,da,b,c,d 可以分成兩組&#xff0c;每組兩個數&#xff0c;滿足每組里面的兩個數一樣&#xff0c;那么稱這四個數是好的。 比如&#xff0c;2,5,2,52,5,2,52,5,2,5 是好的&#xff0c;因它滿足兩組&#xff1a;222 與…

Swift 項目結構詳解:構建可維護的大型應用

Swift 項目結構詳解&#xff1a;構建可維護的大型應用一、基礎結構&#xff08;推薦新手使用&#xff09;二、組件化結構&#xff08;企業級應用推薦&#xff09;層級架構&#xff1a;MVVM Coordinator路由實現&#xff08;Coordinator模式&#xff09;三、通用組件實現DI&…

【實時Linux實戰系列】基于實時Linux的數字轉換器設計

在現代電子系統中&#xff0c;數字轉換器&#xff08;如模數轉換器ADC和數模轉換器DAC&#xff09;扮演著至關重要的角色。它們負責將模擬信號轉換為數字信號&#xff0c;或將數字信號轉換為模擬信號&#xff0c;從而實現信號的數字化處理和傳輸。在實時系統中&#xff0c;如工…

FastTracker:實時準確的視覺跟蹤

摘要 https://arxiv.org/pdf/2508.14370 傳統的多目標跟蹤(MOT)系統主要設計用于行人跟蹤&#xff0c;通常對其他物體類別的泛化能力有限。本文提出了一種能夠處理多種物體類型的通用跟蹤框架&#xff0c;特別強調在復雜交通場景中的車輛跟蹤。所提出的1方法包含兩個關鍵組件&a…

國產輕量級桌面GIS軟件Snaplayers從入門到精通(20)

國產輕量級桌面GIS軟件Snaplayers實操&#xff1a;打開圖層并顯示屬性信息1、根據數據格式選擇圖層文件2、加載圖層到地圖中&#xff0c;并在左側顯示圖層的屬性表格3、屬性表格分頁顯示Snaplayers研發團隊承諾&#xff1a;國產輕量級桌面GIS軟件Snaplayers永久免費并持續更新