SpringSecurity+jwt+captcha登錄認(rèn)證授權(quán)流程總結(jié)
SpringSecurity+jwt+captcha登錄認(rèn)證授權(quán)總結(jié)
版本信息:
springboot 3.2.0、springSecurity 6.2.0、mybatis-plus 3.5.5
認(rèn)證授權(quán)思路和流程: 未攜帶token,訪問(wèn)登錄接口:
1、用戶登錄攜帶賬號(hào)密碼
2、請(qǐng)求到達(dá)自定義Filter,自定義Filter(如JwtAuthenticationTokenFilter)繼承OncePerRequestFilter(此Filter只會(huì)進(jìn)行一次過(guò)濾,在請(qǐng)求返回時(shí),不會(huì)再進(jìn)行調(diào)用),在SecurityConfig中配置,將自定義Filter添加到過(guò)濾器鏈中并加在UsernamePasswordAuthenticationFilter過(guò)濾器之前
3、自定義Filter邏輯
3.1、查詢到用戶未攜帶token,直接放行,進(jìn)入到后面認(rèn)證流程
3.2、將用戶信息封裝成Authentication對(duì)象,調(diào)用authticate()方法進(jìn)行驗(yàn)證,一直到DaoAuthenticationProvider中,會(huì)調(diào)用UserDetailService的loadUserByUserName()方法;自定義UserDetailServiceImpl繼承UserDetailService接口;重寫(xiě)其loadUserByUserName()方法;查到用戶后,封裝成UserDetail對(duì)象返回
4、調(diào)用passwordEncoder的驗(yàn)證方法進(jìn)行用戶信息的認(rèn)證(一般使用BCryptPasswordEncoder,創(chuàng)建此Bean并放入容器中)
5、認(rèn)證通過(guò)后,使用JWT創(chuàng)建token并放到redis中;返回token到前端
攜帶token訪問(wèn):
1、請(qǐng)求到達(dá)自定義Filter中,獲取token,先驗(yàn)證token的正確性,從token中獲取到userId,根據(jù)userId從redis中獲取用戶信息,將用戶信息封裝成Authentication對(duì)象,放進(jìn)SecurityContextHolder中,后面的過(guò)濾器會(huì)在SecurityContextHolder中獲取用戶的信息
2、請(qǐng)求到達(dá)FilterSecurityInterceptor,在springSecurity中默認(rèn)使用FilterSecurityInterceptor來(lái)進(jìn)行權(quán)限校驗(yàn);FilterSecurityInterceptor會(huì)從SecurityContextHolder中獲取Authentication,然后獲取其中的權(quán)限信息,判斷當(dāng)前用戶是否擁有當(dāng)前資源的訪問(wèn)權(quán)限;
3、通過(guò)權(quán)限判斷,獲取資源返回給前端;
登錄認(rèn)證流程圖:
基于RBAC的授權(quán)控制: RBAC概念:
Role-Based Access Control,中文意思是:基于角色(Role)的訪問(wèn)控制。這是一種廣泛應(yīng)用于計(jì)算機(jī)系統(tǒng)和網(wǎng)絡(luò)安全領(lǐng)域的訪問(wèn)控制模型。
簡(jiǎn)單來(lái)說(shuō),就是通過(guò)將權(quán)限分配給角色,再將角色分配給用戶,來(lái)實(shí)現(xiàn)對(duì)系統(tǒng)資源的訪問(wèn)控制。一個(gè)用戶擁有若干角色,每一個(gè)角色擁有若干權(quán)限。這樣,就構(gòu)造成“用戶-角色-權(quán)限”的授權(quán)模型。在這種模型中,用戶與角色之間,角色與權(quán)限之間,一般是多對(duì)多的關(guān)系
模型:
在數(shù)據(jù)庫(kù)中主要體現(xiàn)用戶表、角色表、菜單表、用戶角色關(guān)聯(lián)表、角色菜單關(guān)聯(lián)表五個(gè)模型:
CREATE TABLE "mySchema"."t_user" ( "id" INT IDENTITY(1, 1) NOT NULL, "username" VARCHAR(100) NOT NULL, "password" VARCHAR(100) NOT NULL, "email" VARCHAR(100) NOT NULL, "phone" VARCHAR(100), UNIQUE("id"), UNIQUE("username"), UNIQUE("email"), UNIQUE("phone"), NOT CLUSTER PRIMARY KEY("id")) STORAGE(ON "MAIN", CLUSTERBTR) ; COMMENT ON TABLE "mySchema"."t_user" IS '用戶表'; COMMENT ON COLUMN "mySchema"."t_user"."id" IS '用戶id'; COMMENT ON COLUMN "mySchema"."t_user"."username" IS '用戶名'; COMMENT ON COLUMN "mySchema"."t_user"."password" IS '密碼'; COMMENT ON COLUMN "mySchema"."t_user"."email" IS '用戶郵箱'; COMMENT ON COLUMN "mySchema"."t_user"."phone" IS '電話'; CREATE TABLE "mySchema"."sys_role" ( "id" BIGINT IDENTITY(1, 1) NOT NULL, "name" VARCHAR(128) DEFAULT NULL, "role_key" VARCHAR(100) DEFAULT NULL, "status" CHAR(1) DEFAULT '0', "del_flag" INT DEFAULT 0, "create_by" BIGINT DEFAULT NULL, "create_time" DATETIME(6) DEFAULT NULL, "update_by" BIGINT DEFAULT NULL, "update_time" DATETIME(6) DEFAULT NULL, "remark" VARCHAR(500) DEFAULT NULL, NOT CLUSTER PRIMARY KEY("id")) STORAGE(ON "MAIN", CLUSTERBTR) ; COMMENT ON TABLE "mySchema"."sys_role" IS '角色表'; COMMENT ON COLUMN "mySchema"."sys_role"."id" IS '角色id'; COMMENT ON COLUMN "mySchema"."sys_role"."name" IS '角色名稱(chēng)'; COMMENT ON COLUMN "mySchema"."sys_role"."role_key" IS '角色權(quán)限字符串'; COMMENT ON COLUMN "mySchema"."sys_role"."status" IS '角色狀態(tài)(0正常, 1停用)'; COMMENT ON COLUMN "mySchema"."sys_role"."del_flag" IS '刪除標(biāo)志(0未刪除,1已刪除)'; COMMENT ON COLUMN "mySchema"."sys_role"."remark" IS '備注'; CREATE TABLE "mySchema"."sys_menu" ( "id" BIGINT IDENTITY(2, 1) NOT NULL, "menu_name" VARCHAR(64) DEFAULT 'NULL' NOT NULL, "path" VARCHAR(200) DEFAULT NULL, "component" VARCHAR(50) DEFAULT NULL, "visible" CHAR(1) DEFAULT '0', "status" CHAR(1) DEFAULT '0', "perms" VARCHAR(100) DEFAULT NULL, "icon" VARCHAR(100) DEFAULT '#', "create_by" BIGINT DEFAULT NULL, "create_time" DATETIME(6) DEFAULT CURRENT_TIMESTAMP, "update_by" BIGINT DEFAULT NULL, "update_time" DATETIME(6) DEFAULT CURRENT_TIMESTAMP, "delete_f1ag" INT DEFAULT 0, "remark" VARCHAR(500) DEFAULT NULL, UNIQUE("id"), NOT CLUSTER PRIMARY KEY("id")) STORAGE(ON "MAIN", CLUSTERBTR) ; COMMENT ON TABLE "mySchema"."sys_menu" IS '菜單表'; COMMENT ON COLUMN "mySchema"."sys_menu"."menu_name" IS '菜單名'; COMMENT ON COLUMN "mySchema"."sys_menu"."path" IS '路由地址'; COMMENT ON COLUMN "mySchema"."sys_menu"."component" IS '組件路徑'; COMMENT ON COLUMN "mySchema"."sys_menu"."visible" IS '菜單狀態(tài)(0顯示1隱藏)'; COMMENT ON COLUMN "mySchema"."sys_menu"."status" IS '菜單狀態(tài)(0正常1停用)'; COMMENT ON COLUMN "mySchema"."sys_menu"."perms" IS '權(quán)限標(biāo)識(shí)'; COMMENT ON COLUMN "mySchema"."sys_menu"."icon" IS '菜單圖標(biāo)'; COMMENT ON COLUMN "mySchema"."sys_menu"."delete_f1ag" IS '是否刪除(0未刪除1已刪除)'; COMMENT ON COLUMN "mySchema"."sys_menu"."remark" IS '備注'; CREATE TABLE "mySchema"."sys_user_role" ( "user_id" BIGINT DEFAULT 0 NOT NULL, "role_id" BIGINT DEFAULT 0 NOT NULL, NOT CLUSTER PRIMARY KEY("user_id", "role_id")) STORAGE(ON "MAIN", CLUSTERBTR) ; COMMENT ON TABLE "mySchema"."sys_user_role" IS '用戶角色表'; COMMENT ON COLUMN "mySchema"."sys_user_role"."user_id" IS '用戶id'; COMMENT ON COLUMN "mySchema"."sys_user_role"."role_id" IS '角色id'; CREATE TABLE "mySchema"."sys_role_menu" ( "role_id" BIGINT DEFAULT 0 NOT NULL, "menu_id" BIGINT DEFAULT 0 NOT NULL, NOT CLUSTER PRIMARY KEY("role_id", "menu_id")) STORAGE(ON "MAIN", CLUSTERBTR) ; COMMENT ON TABLE "mySchema"."sys_role_menu" IS '角色菜單關(guān)聯(lián)表'; COMMENT ON COLUMN "mySchema"."sys_role_menu"."role_id" IS '角色id'; COMMENT ON COLUMN "mySchema"."sys_role_menu"."menu_id" IS '菜單id';
權(quán)限流程:
用戶表、角色表、菜單表、部門(mén)表、用戶角色關(guān)聯(lián)表、角色菜單關(guān)聯(lián)表;數(shù)據(jù)權(quán)限分為5種:個(gè)人、全部、本部門(mén)、指定部門(mén)、本部門(mén)及以下
鏈路:
1、用戶注冊(cè),指定部門(mén);創(chuàng)建角色,給角色賦菜單權(quán)限、數(shù)據(jù)權(quán)限;給用戶賦予角色;
2、用戶登錄,通過(guò)用戶的所有角色,獲取所有的菜單權(quán)限和最大數(shù)據(jù)權(quán)限;菜單數(shù)據(jù)直接展示在頁(yè)面;所有數(shù)據(jù)都有創(chuàng)建人,則數(shù)據(jù)的部門(mén)id是創(chuàng)建人的部門(mén)id;
3、在訪問(wèn)數(shù)據(jù)接口時(shí),通過(guò)mybatis的攔截器對(duì)用戶的數(shù)據(jù)進(jìn)行過(guò)濾(拼接sql),返回給前端;
captcha圖片驗(yàn)證碼:
實(shí)現(xiàn)思路:
1、圖片驗(yàn)證碼獲取,并將驗(yàn)證碼驗(yàn)證碼存放起來(lái)(單機(jī)應(yīng)用可放在session中,分布式應(yīng)用放在redis中)
2、當(dāng)用戶登錄時(shí),攜帶驗(yàn)證碼以及驗(yàn)證碼的key,進(jìn)入到后端從redis中獲取值進(jìn)行驗(yàn)證
<!-- 谷歌kaptcha驗(yàn)證碼依賴 --> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>
代碼思路:
1、生成驗(yàn)證碼
public Result<CaptchaVO> getCaptcha() throws IOException { // 生成文字驗(yàn)證碼 String content = defaultKaptcha.createText(); // 生成圖片驗(yàn)證碼 ByteArrayOutputStream outputStream = null; BufferedImage image = defaultKaptcha.createImage(content); outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", outputStream); // 對(duì)字節(jié)數(shù)組Base64編碼 String str = "data:image/jpeg;base64,"; String base64Img = str + Base64.getEncoder().encodeToString(outputStream.toByteArray()).replace("\n", "").replace("\r", ""); CaptchaVO captchaVO = captchaService.cacheCaptcha(content); captchaVO.setBase64Img(base64Img); return Result.success(captchaVO); }
2、存儲(chǔ)驗(yàn)證碼
@Service public class CaptchaService { private Long timeout = 300L; @Autowired private RedisUtils redisUtils; private final String CAPTCHA_KEY_PREFIX = "captcha:verification:"; public CaptchaVO cacheCaptcha(String captcha){ //生成一個(gè)隨機(jī)標(biāo)識(shí)符 String randomStr = UUID.randomUUID().toString(); //緩存驗(yàn)證碼并設(shè)置過(guò)期時(shí)間 String captchaKey = CAPTCHA_KEY_PREFIX.concat(randomStr); redisUtils.set(captchaKey, captcha, timeout, TimeUnit.SECONDS); CaptchaVO captchaVO = new CaptchaVO(); captchaVO.setCaptchaKey(captchaKey); captchaVO.setExpire(timeout); captchaVO.setCaptcha(captcha); return captchaVO; } }
3、校驗(yàn)驗(yàn)證碼
Object captcha = redisUtils.get(reqVO.getCaptchaKey()); if (Objects.isNull(captcha)) { return "驗(yàn)證碼已過(guò)期"; } if (!captcha.equals(reqVO.getCaptcha())) { return "驗(yàn)證碼錯(cuò)誤"; }
到此這篇關(guān)于SpringSecurity+jwt+captcha登錄認(rèn)證授權(quán)總結(jié)的文章就介紹到這了,更多相關(guān)SpringSecurity jwt captcha認(rèn)證授權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringSecurity6.0 如何通過(guò)JWTtoken進(jìn)行認(rèn)證授權(quán)
- springSecurity自定義登錄接口和JWT認(rèn)證過(guò)濾器的流程
- SpringSecurity+Redis+Jwt實(shí)現(xiàn)用戶認(rèn)證授權(quán)
- SpringBoot整合SpringSecurity和JWT和Redis實(shí)現(xiàn)統(tǒng)一鑒權(quán)認(rèn)證
- SpringSecurity+jwt+redis基于數(shù)據(jù)庫(kù)登錄認(rèn)證的實(shí)現(xiàn)
- SpringBoot+SpringSecurity+JWT實(shí)現(xiàn)系統(tǒng)認(rèn)證與授權(quán)示例
- SpringBoot整合SpringSecurity實(shí)現(xiàn)JWT認(rèn)證的項(xiàng)目實(shí)踐
- SpringSecurity整合jwt權(quán)限認(rèn)證的全流程講解
- SpringSecurity構(gòu)建基于JWT的登錄認(rèn)證實(shí)現(xiàn)
- SpringSecurity JWT基于令牌的無(wú)狀態(tài)認(rèn)證實(shí)現(xiàn)
相關(guān)文章
詳解Spring Boot中使用AOP統(tǒng)一處理Web請(qǐng)求日志
本篇文章主要介紹了詳解Spring Boot中使用AOP統(tǒng)一處理Web請(qǐng)求日志,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Java工程mybatis實(shí)現(xiàn)多表查詢過(guò)程詳解
這篇文章主要介紹了Java工程mybatis實(shí)現(xiàn)多表查詢過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Java實(shí)現(xiàn)簡(jiǎn)單的學(xué)生教師管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的學(xué)生教師管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02MyBatis-Plus中如何使用ResultMap的方法示例
本文主要介紹了MyBatis-Plus中如何使用ResultMap,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11使用Spring的注解方式實(shí)現(xiàn)AOP實(shí)例
本篇文章主要介紹了使用Spring的注解方式實(shí)現(xiàn)AOP實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06Spring?Security過(guò)濾器鏈加載執(zhí)行流程源碼解析
Spring?Boot?對(duì)于?Spring?Security?提供了自動(dòng)化配置方案,可以使用更少的配置來(lái)使用?Spring?Security。那么這個(gè)過(guò)濾器鏈?zhǔn)窃趺醇虞d和實(shí)現(xiàn)攔截的呢,對(duì)Spring?Security過(guò)濾器鏈加載執(zhí)行流程感興趣的朋友一起看看吧2021-12-12springBoot2.6.2自動(dòng)裝配之注解源碼解析
對(duì)于springboot個(gè)人認(rèn)為它就是整合了各種組件,然后提供對(duì)應(yīng)的自動(dòng)裝配和啟動(dòng)器(starter),基于這個(gè)流程去實(shí)現(xiàn)一個(gè)定義的裝配組件,下面這篇文章主要給大家介紹了關(guān)于springBoot2.6.2自動(dòng)裝配之注解源碼解析的相關(guān)資料,需要的朋友可以參考下2022-01-01Java的PriorityBlockingQueue優(yōu)先級(jí)阻塞隊(duì)列代碼實(shí)例
這篇文章主要介紹了Java的PriorityBlockingQueue優(yōu)先級(jí)阻塞隊(duì)列代碼實(shí)例,PriorityBlockingQueue顧名思義是帶有優(yōu)先級(jí)的阻塞隊(duì)列,為了實(shí)現(xiàn)按優(yōu)先級(jí)彈出數(shù)據(jù),存入其中的對(duì)象必須實(shí)現(xiàn)comparable接口自定義排序方法,需要的朋友可以參考下2023-12-12