詳解Spring?Security怎么從數(shù)據(jù)庫加載我們的用戶
本章內(nèi)容
- 如何從數(shù)據(jù)庫中讀取用戶對象
- 源碼分析
如何從數(shù)據(jù)庫中讀取用戶對象?
1前面我們分析認(rèn)證的時候就會發(fā)現(xiàn)他在DaoAuthenticationProvider中就已經(jīng)有從數(shù)據(jù)庫中獲取用戶名和密碼的。

public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
意思是說我們只要重寫這個類,就可以從我們所想要的地方獲取用戶信息。
那我們重寫吧
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對象轉(zhuǎn)換成我們所需要的users對象。比較麻煩。
小白: "為什么不直接使用spring security那里面的users對象呢?"
小黑: "我們自己實現(xiàn)users對象的話就可以在自定義。特別是權(quán)限這邊,我們肯定是需要自定義users對象的,不能直接使用其內(nèi)部的對象。"
然后我們直接繼承UserDetails接口,然后自定義一些我們所需要的屬性。說白了就是從內(nèi)置users對象中拷貝一些代碼出來。
小黑: "這里我故意留下了一個坑,如果你真的從內(nèi)置的users對象里的話,你會發(fā)現(xià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主動調(diào)用我們自定義的類呢?"
小黑: "我們要使用自定義的類的話,無非是在DaoAuthenticationProvider里面設(shè)置。所以我們只要找到setUserDetailsService這個函數(shù)就行了??匆幌掠心男┓椒ㄕ{(diào)用了這個函數(shù)?"

看了一下發(fā)現(xiàn)這邊調(diào)用了setUserDetailsService方法的。前面的UserDetailsService是對象是通過spring bean上下文面拿出來的。
那我們也可以效仿他的方式,在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)證成功后訪問
.permitAll() // 白名單
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login") // 注銷成功后訪問
.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ù)庫表結(jié)構(gòu)呢? "
小黑: "我忘了, 我找找在哪可以抄"
在分析UserDetailsService的時候, 我們一般都要看看他的類族是怎樣的, 結(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無法執(zhí)行
結(jié)果發(fā)現(xiàn), 就這...
我還不如自己設(shè)計呢
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ù) RBAC 設(shè)計
小白: "這樣就完成了?"
小黑: "完成了, 其他代碼都是mybatis生成的, 簡單"
啟動, 訪問 崩了

等下, spring security脫敏呢? 為什么這里可以輸出密碼?
分析源碼看看
脫敏為什么沒有生效?
通過源碼分析,脫敏應(yīng)該是認(rèn)證成功之后的事情
大概看了下源碼, ProviderManager有脫敏, 但是為什么不生效呢?


看這代碼的意思, 是要我們在 Users 類上多添加一個接口CredentialsContainer
public class Users implements Serializable, UserDetails, CredentialsContainer {
@Override
public void eraseCredentials() {
// 設(shè)置 password 為 null
this.password = null;
}
}
行, 前面的坑補(bǔ)上了。再試試

完美~~~
總結(jié)
記住UserDetailsService怎么自定義, 注意自定義的User需要實現(xiàn)哪些接口, 還有脫敏問題
以上就是詳解Spring Security怎么從數(shù)據(jù)庫加載我們的用戶的詳細(xì)內(nèi)容,更多關(guān)于Spring Security數(shù)據(jù)庫加載用戶的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Idea使用插件實現(xiàn)逆向工程搭建SpringBoot項目的圖文教程
這篇文章主要介紹了Idea使用插件實現(xiàn)逆向工程搭建SpringBoot項目,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
java中instanceof和getClass()的區(qū)別分析
本篇文章介紹了,在java中instanceof和getClass()的區(qū)別分析。需要的朋友參考下2013-04-04
java中mybatis和hibernate的用法總結(jié)
在本篇文章里小編給大家整理的是一篇關(guān)于java中mybatis和hibernate的用法總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。2021-01-01
Springboot項目Mybatis升級為Mybatis-Plus的詳細(xì)步驟
在許多 Java 項目中,MyBatis 是一個廣泛使用的 ORM 框架,然而,隨著 MyBatis-Plus 的出現(xiàn),許多開發(fā)者開始遷移到這個更加簡潔、高效的工具,它在 MyBatis 的基礎(chǔ)上提供了更多的功能,所以本文將介紹Springboot項目Mybatis升級為Mybatis-Plus的詳細(xì)步驟2025-03-03
Java使用ExecutorService來停止線程服務(wù)
這篇文章主要介紹了Java使用ExecutorService來停止線程服務(wù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04

