詳解Spring?Security怎么從數(shù)據(jù)庫(kù)加載我們的用戶
本章內(nèi)容
- 如何從數(shù)據(jù)庫(kù)中讀取用戶對(duì)象
- 源碼分析
如何從數(shù)據(jù)庫(kù)中讀取用戶對(duì)象?
1前面我們分析認(rèn)證的時(shí)候就會(huì)發(fā)現(xiàn)他在DaoAuthenticationProvider
中就已經(jīng)有從數(shù)據(jù)庫(kù)中獲取用戶名和密碼的。
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
意思是說(shuō)我們只要重寫這個(gè)類,就可以從我們所想要的地方獲取用戶信息。
那我們重寫吧
public class MybatisUserDetailsService implements UserDetailsService, UserDetailsPasswordService { @Resource private UsersMapper usersMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Users users = usersMapper.loadUserByUsername(username); if (null == users) { throw new UsernameNotFoundException(username); } return users; } @Override public UserDetails updatePassword(UserDetails user, String newPassword) { if (user instanceof Users users) { users.setPassword(newPassword); usersMapper.updateByPrimaryKeySelective(users); } return user; } }
發(fā)現(xiàn)這里需要把spring security的users
對(duì)象轉(zhuǎn)換成我們所需要的users
對(duì)象。比較麻煩。
小白: "為什么不直接使用spring security那里面的users對(duì)象呢?"
小黑: "我們自己實(shí)現(xiàn)users對(duì)象的話就可以在自定義。特別是權(quán)限這邊,我們肯定是需要自定義users對(duì)象的,不能直接使用其內(nèi)部的對(duì)象。"
然后我們直接繼承UserDetails
接口,然后自定義一些我們所需要的屬性。說(shuō)白了就是從內(nèi)置users
對(duì)象中拷貝一些代碼出來(lái)。
小黑: "這里我故意留下了一個(gè)坑,如果你真的從內(nèi)置的users
對(duì)象里的話,你會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題。"
@Data @Builder @AllArgsConstructor @NoArgsConstructor public class Users implements Serializable, UserDetails { private Long id; private String username; private String password; private Boolean enabled; private Boolean accountnonexpired; private Boolean accountnonlocked; private Boolean credentialsnonexpired; private List<Roles> rolesList; private static final long serialVersionUID = 1L; @Override public Collection<? extends GrantedAuthority> getAuthorities() { ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Roles roles : getRolesList()) { authorities.add(new SimpleGrantedAuthority(roles.getRole())); } return authorities; } @Override public boolean isAccountNonExpired() { return accountnonexpired; } @Override public boolean isAccountNonLocked() { return accountnonlocked; } @Override public boolean isCredentialsNonExpired() { return credentialsnonexpired; } @Override public boolean isEnabled() { return enabled; } }
小白: "代碼定義好了,但現(xiàn)在你要怎么加入到spring security中呢?讓spring security主動(dòng)調(diào)用我們自定義的類呢?"
小黑: "我們要使用自定義的類的話,無(wú)非是在DaoAuthenticationProvider
里面設(shè)置。所以我們只要找到setUserDetailsService
這個(gè)函數(shù)就行了??匆幌掠心男┓椒ㄕ{(diào)用了這個(gè)函數(shù)?"
看了一下發(fā)現(xiàn)這邊調(diào)用了setUserDetailsService
方法的。前面的UserDetailsService是對(duì)象是通過(guò)spring bean上下文面拿出來(lái)的。
那我們也可以效仿他的方式,在spring bean上添加我們的UserDetailsService
Bean。
@Bean public UserDetailsService userDetailsService() { return new MybatisUserDetailsService(); }
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity.authorizeHttpRequests() .anyRequest().authenticated() .and() .formLogin() .defaultSuccessUrl("/hello", true) // 認(rèn)證成功后訪問(wèn) .permitAll() // 白名單 .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/login") // 注銷成功后訪問(wèn) .clearAuthentication(true) .invalidateHttpSession(true) .and() .build(); }
@GetMapping("hello") public HashMap<String, Object> hello(Authentication authentication) { HashMap<String, Object> map = new HashMap<>(); Object principal = authentication.getPrincipal(); String name = authentication.getName(); map.put("principal", principal); map.put("name", name); return map; }
小白: "停一下, 你數(shù)據(jù)庫(kù)表結(jié)構(gòu)呢? "
小黑: "我忘了, 我找找在哪可以抄"
在分析UserDetailsService
的時(shí)候, 我們一般都要看看他的類族是怎樣的, 結(jié)果發(fā)現(xiàn)可以偷懶的地方, 也就是表結(jié)構(gòu)的位置
create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null); create table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username)); create unique index ix_auth_username on authorities (username,authority);
這段sql
要改改, 否則mysql
無(wú)法執(zhí)行
結(jié)果發(fā)現(xiàn), 就這...
我還不如自己設(shè)計(jì)呢
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for authorities -- ---------------------------- DROP TABLE IF EXISTS `authorities`; CREATE TABLE `authorities` ( `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, UNIQUE INDEX `ix_auth_username`(`username` ASC, `authority` ASC) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for users -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `enabled` bit(1) NULL DEFAULT NULL, `accountNonExpired` bit(1) NULL DEFAULT b'1', `accountNonLocked` bit(1) NULL DEFAULT b'1', `credentialsNonExpired` bit(1) NULL DEFAULT b'1', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
注意, 這里僅僅只是為了玩耍, 而非企業(yè), 在企業(yè)中, 肯定不是這么設(shè)計(jì)的, 一般根據(jù) RBAC
設(shè)計(jì)
小白: "這樣就完成了?"
小黑: "完成了, 其他代碼都是mybatis
生成的, 簡(jiǎn)單"
啟動(dòng), 訪問(wèn) 崩了
等下, spring security脫敏呢? 為什么這里可以輸出密碼?
分析源碼看看
脫敏為什么沒(méi)有生效?
通過(guò)源碼分析,脫敏應(yīng)該是認(rèn)證成功之后的事情
大概看了下源碼, ProviderManager
有脫敏, 但是為什么不生效呢?
看這代碼的意思, 是要我們?cè)?Users
類上多添加一個(gè)接口CredentialsContainer
public class Users implements Serializable, UserDetails, CredentialsContainer { @Override public void eraseCredentials() { // 設(shè)置 password 為 null this.password = null; } }
行, 前面的坑補(bǔ)上了。再試試
完美~~~
總結(jié)
記住UserDetailsService
怎么自定義, 注意自定義的User
需要實(shí)現(xiàn)哪些接口, 還有脫敏問(wèn)題
以上就是詳解Spring Security怎么從數(shù)據(jù)庫(kù)加載我們的用戶的詳細(xì)內(nèi)容,更多關(guān)于Spring Security數(shù)據(jù)庫(kù)加載用戶的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng)的示例代碼
這篇文章主要介紹了如何利用Java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng),可以實(shí)現(xiàn)存款,取款,查詢等功能,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-09-09SpringBoot整合Docker實(shí)現(xiàn)一次構(gòu)建到處運(yùn)行的操作方法
本文講解的是 SpringBoot 引入容器化技術(shù) Docker 實(shí)現(xiàn)一次構(gòu)建到處運(yùn)行,包括鏡像構(gòu)建、Docker倉(cāng)庫(kù)搭建使用、Docker倉(cāng)庫(kù)可視化UI等內(nèi)容,需要的朋友可以參考下2022-10-10Spring cloud踩坑記錄之使用feignclient遠(yuǎn)程調(diào)用服務(wù)404的方法
這篇文章主要給大家介紹了關(guān)于Spring cloud踩坑記錄之使用feignclient遠(yuǎn)程調(diào)用服務(wù)404的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11Java系統(tǒng)運(yùn)行緩慢等問(wèn)題的排查思路
這篇文章主要介紹了Java系統(tǒng)運(yùn)行緩慢等問(wèn)題的排查思路,讀者可以根據(jù)具體情況具體分析,從而解決問(wèn)題2021-04-04Java數(shù)據(jù)結(jié)構(gòu)之并查集的實(shí)現(xiàn)
并查集是一種用來(lái)管理元素分組情況的數(shù)據(jù)結(jié)構(gòu)。并查集可以高效地進(jìn)行如下操作。本文將通過(guò)Java實(shí)現(xiàn)并查集,感興趣的小伙伴可以了解一下2022-01-01spring-data-redis自定義實(shí)現(xiàn)看門狗機(jī)制
redission看門狗機(jī)制是解決分布式鎖的續(xù)約問(wèn)題,本文主要介紹了spring-data-redis自定義實(shí)現(xiàn)看門狗機(jī)制,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03Feign?日期格式轉(zhuǎn)換錯(cuò)誤的問(wèn)題
這篇文章主要介紹了Feign?日期格式轉(zhuǎn)換錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03