日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

手把手带你撸一把springsecurity框架源码中的认证流程

發布時間:2024/10/5 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 手把手带你撸一把springsecurity框架源码中的认证流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

提springsecurity之前,不得不說一下另外一個輕量級的安全框架Shiro,在springboot未出世之前,Shiro可謂是頗有統一J2EE的安全領域的趨勢。

有關shiro的技術點

1、shiro之權限管理的概念
2、shiro之第一個程序認證
3、shiro之自定義realm
4、shiro認證+授權(使用MD5+salt加密)
5、shiro+springboot 圖解分析思路
6、Shiro+springboot+mybatis(md5+salt+散列)認證與授權-01
7、Shiro+springboot+mybatis(md5+salt+散列)認證與授權-02
8、Shiro+springboot+mybatis+EhCache(md5+salt+散列)認證與授權-03



一、springsecurity因springboot而火

1.1.

Spring Security 并非一個新生的事物,它最早不叫 Spring Security ,叫 Acegi Security,叫 Acegi Security 并不是說它和 Spring 就沒有關系了,它依然是為 Spring 框架提供安全支持的。事實上,Java 領域的框架,很少有框架能夠脫離 Spring 框架獨立存在。(spring真的是碉堡了呀)

1.2.

當 Spring Security 還叫 Acegi Security 的時候,流傳著這樣一句話,“每當有人要使用 Acegi Security,就會有一個精靈死去”,從這你就可以感覺到,其中的配置是多繁瑣;

1.3.

之后Acegi Security投入了spring的窩里,(呵,這波操作可以呀),然后你懂的,研發團隊的小凸凸們開始精簡繁雜的xml配置;雖然比之前簡化了很多,但一直沒火起來,(這一波spring只能在心里默默的說:你是真的帶不動,啥也不是;)

1.4.

直到有一天,springboot這個二愣子突然出現在了封建社會中,徹底顛覆了J2EE的世界,“約定大于配置 ”成為springboot的代名詞。一人得道,雞犬升天,連帶著把spring家族的產品都帶了一把,springsecurity就是其中之一。

1.5.

當前springboot/springcloud是J2EE中主流的技術棧(springboot不是一個新的框架,他是對spring的擴展,是為了高效開發而生)
spring是對Java代碼的封裝,springboot可以說又對spring進行了封裝,屏蔽了內部的細節,讓開發人員專注于業務邏輯;缺點就是封裝太深,學習成本高。

推薦兩種搭配
1.springboot+springcloud+springsecurity
2.SSM+shiro



二、代碼準備工作


2.1.使用初始化向導快速搭建springboot項目

2.2.編寫mapper、dao、文件

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itz.security.mapper.UserMapper"><select id="findPasswordByUsername" resultType="Users">select * from users where username=#{usernaem}</select> </mapper>

UserMapper接口

public interface UserMapper {Users findPasswordByUsername(String username); }

2.3.service層編寫

至于為什么要實現UserDetailService這個接口,后面會詳細說

package com.itz.security.service;import com.itz.security.entity.Users; import com.itz.security.mapper.UserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/24* @description:*/ @Service(value = "userDetailService") @Slf4j public class MyUserDetailService implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {log.info("用戶名:"+username);Users user = userMapper.findPasswordByUsername(username);if (user == null) {throw new UsernameNotFoundException("沒有該賬戶!");}String password = passwordEncoder.encode(user.getPassword());log.info("加密后的密碼為:"+password);return new User(user.getUsername(),password, AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole()));} }

2.4.實體類編寫

只是簡單的模擬,就沒必要太復雜。
注意:不要使用User這個名字,security中有這個類,防止沖突。

@Data public class Users {private Integer id;private String username;private String password;private String role; }

2.5.controller層編寫

@RestController public class HelloController {@GetMapping("/test")public String hello() {return "HELLO";} }

2.6.login.html

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><form action="login.html" method="post">username: <input type="text" name="username"> <br>password: <input type="password" name="password"> <br><input type="submit" value="提交"></form> </body> </html>

2.7.編寫SecurityConfig文件

至于為什么是這樣,后面源碼分析時會說

package com.itz.security.controller;import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/24* @description:*/ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/login.html").and().csrf().disable();//關閉csrf(一種web攻擊手段)}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/js/**","/css/**","/images/**");} }

2.8.application.yml編寫

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverpassword: hao20001010username: rooturl: jdbc:mysql://localhost:3306/crud?serverTimezone=UTCmybatis:mapper-locations: /mapper/**type-aliases-package: com.itz.security.entity

2.9.數據庫表設計

注意掃描mapper接口



三、源碼認證流程分析


首先會使用debug調試,最基本的F7和F8以及F9,當然鼠標點擊也一樣
F7:調試的時候遇到方法體會進入到方法體內部執行
F8:遇到方法體不會進入到方法體內部,只會依次執行
F9:只會執行打斷點的地方

3.1. ctrl+N 查看UsernamePasswordAuthenticationFilter類的源碼,在attemptAuthentication方法上打上斷點,bebug模式下啟動


當走到82行,這個令牌類UsernamePasswordAuthenticationToken

點擊F7 debug進入該類中查看執行情況

你可以把這個令牌類UsernamePasswordAuthenticationToken當作一個實體類,用來將前端傳來的變量賦值給本地變量;簡單了說就是將其封裝

繼續F8之后,就會返回到UsernamePasswordAuthenticationFilter類中

點擊F7,進入查看setDetails方法的源碼

擊F7查看buildDetails方法

擊F7查看WebAuthenticationDetails類的具體細節

原來這個setDetails方法主要是為了將請求中的額外信息保存起來。


下面要進入AbstractUserDetailsAuthenticationProvider類了


點擊F7查看authenticate方法的具體實現細節

通過AuthenticationProvicer接口的實現類獲取用戶的登錄方式,然后通過for循環,查看是否支持該登錄方式;(一般的登錄方式有vx,qq,表單等)

如果不支持支持該登錄方式你會發現

parentResult = this.parent.authenticate(authentication);

從表面意思我們也可以猜到,調用父級提供Provider,重新執行該authenticate方法,看是否支持該登錄方式


當支持該登錄方式之后,

然后父類會調用authenticate對用戶的身份進行認證(也就是那個支持登錄方式的父類)


F7查看具體的認證細節


重點來了

user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);

retrieveUser是AbstractUserDetailsAuthenticationProvider抽象類的繼承類DaoAuthenticationProvider類中的方法

作用是從數據庫或者緩存中獲取用戶信息

F7查看源碼

UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);

可以發現它加載了我自定義的MyUserDetailService類,為什么呢?當然是因為我實現了UserDetailService接口,接下來的debug到哪個類你也因該明白了(多態)

沒錯就是我自定義的MyUserDetailService類

還是自己寫的代碼香哈😀

在39行F7進入查看源碼,然后一直F8返回到上一級的調用處


return loadedUser執行之后就會返回到AbstractUserDetailsAuthenticationProvider類中的authenticate方法中

this.preAuthenticationChecks.check(user);

這個方法主要是對用戶狀態進行檢測,看是否可用,過期,鎖定等

下面是它的方法



matches方法具體源碼如下,其中BCrypt.checkpw中的方法不再展示,主要是干嘛的,下面已說明



放行



自此認證流程就分析結束了

總結認證流程

1.首先進入到UsernamePasswordAuthenticationFilter類中的attemptAuthentication方法

1.1.將用戶登錄信息封裝到UsernamePasswordToken令牌中
1.2.setDetails將請求中額外的信息封裝到WebAuthenticaitonDetails
1.3.調用AuthenticationManager接口的實現類ProviderManager中的authenticate方法

2.在ProviderManager類中的authenticate進行執行

2.1.匹配支持的登錄方式
2.2.匹配成功后,進入到AuthenticationProvider接口的實現類AbstractUserDetailsAuthenticationProvider中的authenticate方法
2.3.在authenticate方法中,獲取用戶認證信息,然后進行校驗是否過期,最后進行密碼的匹配

簡單了說就上面兩點,但是細節還是有很多的😁😁

希望有服務端大佬指點迷津😙

總結

以上是生活随笔為你收集整理的手把手带你撸一把springsecurity框架源码中的认证流程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。