SpringSecurity獲取當(dāng)前登錄用戶的信息的幾種方法實現(xiàn)
在 Spring Security 中,當(dāng)前登錄用戶的信息實際上都存儲在與當(dāng)前線程綁定的 SecurityContext 中,里面保存了一個 Authentication 對象。你展示的這幾種寫法,其實都是不同的方式來獲取這個 Authentication 對象或者從中提取出用戶信息。下面詳細(xì)說明每種方式的原理和適用場景:
1. 直接注入 Principal
@GetMapping("/welcome") @ResponseBody public Object toLoginInfo(Principal principal){ return principal; }
說明:
當(dāng)你在 Controller 方法中直接聲明一個 Principal
類型的參數(shù)時,Spring Security 會自動注入當(dāng)前登錄用戶對應(yīng)的 Principal
對象。通常,Principal
只包含最基本的用戶標(biāo)識(例如用戶名),但如果需要更多信息,就需要使用 Authentication
。
2. 直接注入 Authentication
@GetMapping("/welcome2") @ResponseBody public Object toLoginInfo2(Authentication authentication) { return authentication; }
說明:Authentication
接口繼承自 Principal
,除了包含用戶名外,還包含了用戶的權(quán)限信息(Authorities)、認(rèn)證憑證(Credentials)、認(rèn)證狀態(tài)等。直接注入 Authentication
可以獲取更豐富的信息,是實際開發(fā)中常用的方式之一。
3. 注入 UsernamePasswordAuthenticationToken
@GetMapping("/welcome3") @ResponseBody public Object toLoginInfo3(UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) { return usernamePasswordAuthenticationToken; }
- 說明:
UsernamePasswordAuthenticationToken
是Authentication
的一個常見實現(xiàn),通常在基于表單登錄時使用。它除了保存用戶名和密碼,還保存了用戶權(quán)限等信息。直接注入這種類型的參數(shù),與直接注入Authentication
類似,只是類型更加具體。
4. 通過 SecurityContextHolder 獲取
@GetMapping("/welcome4") @ResponseBody public Object toLoginInfo4() { return SecurityContextHolder.getContext().getAuthentication(); }
說明:這種方式是從靜態(tài)的 SecurityContextHolder
中獲取當(dāng)前線程綁定的 SecurityContext
,再從中取出 Authentication
對象。使用這種方式可以在非 Controller 的地方(例如在業(yè)務(wù)邏輯或工具類中)獲取當(dāng)前登錄用戶的信息。
5. 使用自定義工具方法
@GetMapping("/welcome5") @ResponseBody public Object toLoginInfo5() { return LoginInfoUtil.getCurrentLoginUser(); }
說明:這是一個對上面幾種方式的封裝,通常會在項目中封裝一個工具類(如 LoginInfoUtil
),內(nèi)部封裝對 SecurityContextHolder
的調(diào)用或者其他邏輯處理,返回一個更為友好的用戶信息對象。這樣做有利于集中管理和統(tǒng)一用戶信息的獲取邏輯。
LoginInfoUtil:
public class LoginInfoUtil { /** * 獲取當(dāng)前登錄用戶信息 * @return */ public static TUser getCurrentLoginUser() { return (TUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); } }
總結(jié)
統(tǒng)一存儲:Spring Security 將當(dāng)前認(rèn)證信息統(tǒng)一保存在
SecurityContext
中,通過SecurityContextHolder
實現(xiàn)線程級別的綁定。多種方式:你可以直接通過方法參數(shù)(如
Principal
、Authentication
或更具體的認(rèn)證類)來獲取,也可以在任意位置通過SecurityContextHolder.getContext().getAuthentication()
獲取。這些方式本質(zhì)上都是獲取同一個對象,只是使用方式不同。擴展與封裝:如果在項目中經(jīng)常需要獲取當(dāng)前用戶信息,可以封裝一個工具類,方便調(diào)用和后續(xù)擴展。
Authentication
繼承自 Principal
,這意味著它不僅包含基本的身份信息,還擴展了額外的認(rèn)證細(xì)節(jié)。
UsernamePasswordAuthenticationToken
是 Authentication
接口的一個具體實現(xiàn),專門用于用戶名和密碼認(rèn)證的場景。
在實際認(rèn)證流程中,通常會通過 UsernamePasswordAuthenticationToken
來封裝用戶提交的認(rèn)證信息,并在認(rèn)證成功后返回一個完整的認(rèn)證對象。
如何獲取更多的用戶信息
實體類實現(xiàn)UserDetails接口,實現(xiàn)UserDetails的7個接口,這樣,你既可以保留 Spring Security 要求的七個方法,又可以在實體類中擴展其他的業(yè)務(wù)屬性。
自定義用戶實體類
擴展業(yè)務(wù)屬性:在你的用戶實體類中,可以添加額外的屬性,例如真實姓名、郵箱、電話等等。
實現(xiàn) UserDetails 接口的七個方法:
getAuthorities():返回用戶擁有的權(quán)限(或角色),通常是一個包含 GrantedAuthority 對象的集合。
getPassword():返回用戶的密碼,用于身份驗證。
getUsername():返回用戶名。
isAccountNonExpired():表示用戶賬戶是否未過期。返回 true 表示賬戶有效,未過期。
isAccountNonLocked():表示用戶賬戶是否未被鎖定。返回 true 表示賬戶未被鎖定。
isCredentialsNonExpired():表示用戶的憑證(密碼)是否未過期。
isEnabled():表示用戶賬戶是否可用(啟用狀態(tài))。
之后由于之前是返回UserDetails的實現(xiàn)類User,此時由于我們有自定義的實現(xiàn)類TUser,因此我們可以直接返回了,不需要使用框架的User類了。
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 通過用戶名查詢數(shù)據(jù)庫 TUser user = userMapper.selectByLoginAct(username); if (user == null){ throw new UsernameNotFoundException("用戶不存在"); } return user; // 實現(xiàn)了UserDetails接口,包含所有字段 // // 返回框架User(UserDetails的實現(xiàn)類)但是使用UserDetails接收 // return User.builder() // .username(user.getLoginAct()) // .password(user.getLoginPwd()) // .authorities(AuthorityUtils.NO_AUTHORITIES) // 沒有權(quán)限(權(quán)限管理部分) // .build(); // 把UserDetails(User)返回給框架之后,框架會采用密碼加密器進行密碼的比較 }
如何忽略某些字段(不返回前端)以及規(guī)范日期格式
使用
@JsonIgnore // 表示該字段不返回給前端
如果實體類當(dāng)中日期類較多,每一個字段加上規(guī)范會很麻煩,因此一般會在application.yml當(dāng)中配置Jackson的轉(zhuǎn)換方式:
spring: application: name: Security-04-login-info # jackson配置 時間格式(使用指定的時區(qū)和格式) jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss
登錄成功后的處理:
@Bean// 安全過濾器鏈Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { //httpSecurity方法參數(shù)注入Bean return httpSecurity // 配置自己的登錄頁面 .formLogin( (formLogin) ->{ formLogin.loginProcessingUrl("/login") // 登錄賬戶密碼往哪個地址提交 .loginPage("/toLogin")// 定制登錄頁面 .successForwardUrl("/welcome"); // 登錄成功之后,跳轉(zhuǎn)到哪個頁面,默認(rèn)是跳轉(zhuǎn)到之前的頁面 }) .authorizeHttpRequests((authorizeHttpRequests)->{ authorizeHttpRequests .requestMatchers("/toLogin","/common/captcha").permitAll() // 特殊情況,toLogin不需要登錄就可以訪問, 驗證碼不需要登錄就可以訪問 .anyRequest().authenticated(); // 除了上述特殊情況之外,其他任何請求都需要認(rèn)證之后才能訪問 }) .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class) .build(); }
使用 .successForwardUrl("/welcome") 時,Spring Security 在用戶登錄成功后不會發(fā)起新的 HTTP 請求,而是通過內(nèi)部的請求轉(zhuǎn)發(fā)(forward)的方式,將當(dāng)前請求轉(zhuǎn)發(fā)到指定的 URL(即 "/welcome")。這意味著:
- 內(nèi)部轉(zhuǎn)發(fā): 請求在服務(wù)器內(nèi)部進行轉(zhuǎn)發(fā),不會改變?yōu)g覽器地址欄顯示的 URL。
- 請求方法保持不變: 轉(zhuǎn)發(fā)過程中,原始的 HTTP 請求方法(例如 POST)會被保留。因此,如果登錄請求是 POST,則轉(zhuǎn)發(fā)后的 "/welcome" 接口也是 POST 請求。
- 常用于登錄成功后的處理: 這種方式適合需要在登錄成功后繼續(xù)處理原始請求數(shù)據(jù)或者保持請求上下文的場景。
總結(jié)來說,.successForwardUrl("/welcome") 用于在認(rèn)證成功后,將請求內(nèi)部轉(zhuǎn)發(fā)到 "/welcome" 接口,從而執(zhí)行相應(yīng)的處理邏輯,而不是向瀏覽器發(fā)起新的請求。
到此這篇關(guān)于SpringSecurity獲取當(dāng)前登錄用戶的信息的文章就介紹到這了,更多相關(guān)SpringSecurity獲取當(dāng)前登錄用戶的信息內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC + jquery.uploadify實現(xiàn)上傳文件功能
文件上傳是很多項目都會使用到的功能,SpringMVC當(dāng)然也提供了這個功能。不過小編不建議在項目中通過form表單來提交文件上傳,這樣做的局限性很大。下面這篇文章主要介紹了利用SpringMVC + jquery.uploadify實現(xiàn)上傳文件功能的相關(guān)資料,需要的朋友可以參考下。2017-06-06使用Java編寫控制JDBC連接、執(zhí)行及關(guān)閉的工具類
這篇文章主要介紹了如何使用Java來編寫控制JDBC連接、執(zhí)行及關(guān)閉的程序,包括一個針對各種數(shù)據(jù)庫通用的釋放資源的工具類的寫法,需要的朋友可以參考下2016-03-03如何自定義Jackson序列化?@JsonSerialize
這篇文章主要介紹了如何自定義Jackson序列化?@JsonSerialize,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12