基于數(shù)據(jù)庫的用戶認證實現(xiàn)SpringBoot3整合SpringSecurity6的過程
寫在前面
上一篇文章中,我們了解了SpringSecurity怎么基于內(nèi)存進行用戶認證。但這還遠遠不夠,在實際開發(fā)中。
用戶往往都存在于數(shù)據(jù)庫,所以從這篇文章開始,我們就要開始學(xué)習(xí)基于數(shù)據(jù)庫的用戶認證。
一、認證流程
其實基于數(shù)據(jù)庫的用戶認證和基于內(nèi)存認證大同小異,我們只需要將從內(nèi)存獲取用戶信息,換成從數(shù)據(jù)庫獲取用戶信息即可。
換成代碼就是替換掉InMermoryUserDetailManager 實現(xiàn)類,自己去實現(xiàn)UserDetailsService,從數(shù)據(jù)庫查詢用戶然后返回。

二、SpringBoot3整合數(shù)據(jù)庫
在進行認證前,我們需要保證SpringBoot能正常整合數(shù)據(jù)庫,查詢用戶信息。這里我們使用的數(shù)據(jù)庫是MySQL8.0。
2.1 創(chuàng)建數(shù)據(jù)庫、表、插入數(shù)據(jù)
①創(chuàng)建數(shù)據(jù)庫
我們這里創(chuàng)建一個security-demo的數(shù)據(jù)庫
-- 創(chuàng)建數(shù)據(jù)庫 CREATE DATABASE `security-demo`; USE `security-demo`;
② 創(chuàng)建用戶
-- 創(chuàng)建用戶表 CREATE TABLE `user`( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -- 主鍵ID `username` VARCHAR(50) DEFAULT NULL , -- 用戶名 `password` VARCHAR(500) DEFAULT NULL, -- 密碼 `enabled` BOOLEAN NOT NULL -- 是否啟用 );
③ 創(chuàng)建唯一索引
在這里一個用戶的用戶名
username字段肯定是不能重復(fù)的,所以要為username創(chuàng)建唯一索引,保證username的唯一
CREATE UNIQUE INDEX `user_username_uindex` ON `user`(`username`);
④ 插入數(shù)據(jù)
為了方便測試,我們默認插入幾個用戶。為了安全起見,這里密碼采用
SpringSecurity默認的bcrypt加密方式。不清楚的小伙可以先不用管,再后面的文章中會詳細介紹到密碼加密算法
-- 插入用戶數(shù)據(jù)(密碼是 "password" )
INSERT INTO `user` (`username`, `password`, `enabled`) VALUES
('admin', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('xiezhr', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('xiaofan', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE);2.2 配置Lombok
為了偷懶,我們還引入了Lombok。 使用Lombok,可以讓我們避免寫get、set、toString等樣板式代碼。堪稱偷懶神器,解放了雙手。

① 下面例舉了怎么使用Lombok 來偷懶
- 簡化 Getter 和 Setter 方法:在傳統(tǒng)的 Java 開發(fā)中,你經(jīng)常需要為每個類的屬性手動編寫
Getter和Setter方法,但是有了Lombok,你只需要在屬性上加上@Getter和@Setter注解,Lombok就會為你自動生成這些方法。 - 自動生成構(gòu)造函數(shù):通過
@NoArgsConstructor、@RequiredArgsConstructor或@AllArgsConstructor注解,你可以快速生成無參構(gòu)造函數(shù)、帶有必需參數(shù)的構(gòu)造函數(shù)或者帶有全部參數(shù)的構(gòu)造函數(shù)。 - 自動生成 equals 和 hashCode 方法: 通過
@EqualsAndHashCode注解,Lombok 會根據(jù)類的字段自動生成equals()和hashCode()方法,讓你的類更易于比較和使用在集合中。 - 日志記錄更輕松: 使用
@Slf4j注解,你可以直接在類中使用log對象,而無需手動創(chuàng)建日志記錄器。 - 簡化異常拋出: 通過
@SneakyThrows注解,你可以在方法中拋出受檢異常,而無需顯式地在方法上聲明或捕獲它們。 - 數(shù)據(jù)類簡化: 使用
@Data注解,Lombok 會為你自動生成所有常用方法,如 Getter、Setter、toString()等,讓你的數(shù)據(jù)類更加簡潔。 - 鏈式調(diào)用: 使用
@Builder注解,Lombok 可以幫你創(chuàng)建一個更優(yōu)雅的構(gòu)建器模式,讓你的對象初始化更加流暢。
② IDEA 中安裝 Lombok 插件
我們需要再IDEA中安裝了
Lombok插件,才能正式愉快的使用Lombok。
根據(jù)你的系統(tǒng)依次點擊菜單:
Windows 系統(tǒng):File -> Settings... -> Plugins;
Mac 系統(tǒng):IntelliJ IDEA -> Preferences -> Plugins;

點擊 Marketplace , 進入插件市場, 輸入關(guān)鍵詞 lombok, 搜索該插件:

③ 引入依賴
在pom.xml文件中添加如下依賴
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>2.3 引入Mybatis Plus
為了方便后續(xù)數(shù)據(jù)庫增刪改查等操作,我們引入MybatisPuls作為持久層框架。
① Mybatis Plus簡介
有些小伙伴可能還不知道MybatisPuls,這里簡單介紹一下,知道的小伙伴直接跳過即可。
用白話文說MybatisPuls是一款操作數(shù)據(jù)庫的框架。它是一個 MyBatis 的增強工具,就像 iPhone手機一般都有個 plus 版本一樣,它在 MyBatis 的基礎(chǔ)上只做增強不做改變,為簡化開發(fā)、提高效率而生。
MyBatis Plus 的愿景是成為 MyBatis 最好的搭檔,就像魂斗羅中的 1P、2P,基友搭配,效率翻倍。

② 引入依賴
為了整合mybatis-plus,我們需要在pom.xml中引入如下依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>2.4 引入MySQL依賴
我們這里數(shù)據(jù)庫使用的是MySQL,所以還得引入MySQL相關(guān)依賴
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>2.5 完整依賴
最終完整依賴如下
<dependencies>
<!-- web依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringSecurity 依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.2.10</version>
</dependency>
<!--lombok依賴-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<!--SpringBoot3整合mybatis-plus 依賴-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<!--junit依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--MySQL依賴-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>2.6 配置數(shù)據(jù)源
application.yml文件中配置MySQL數(shù)據(jù)源,即mybatis-plus日志
# MySQL數(shù)據(jù)源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3308/security-demo
username: root
password: 123456
# MySQL日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl2.7 創(chuàng)建實體類
package com.xiezhr.securityindbuser.entity;
@TableName("user")
@Data
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField(value = "username")
private String username;
@TableField(value = "password")
private String password;
@TableField(value = "enabled")
private Boolean enabled;
}@TableName("user"):mybatis-plus注解,用來指定數(shù)據(jù)庫表名。這里實體名稱與數(shù)據(jù)庫表名一致,該注解可省略@Data:Lombok注解,用來生成get、set方法@TableId(value = "id", type = IdType.AUTO):mybatis-plus注解,用來指定了字段id為表的主鍵,同時指定主鍵為自增類型@TableField(value = "username"):mybatis-plus注解,用來指定字段名。這里實體類屬性與數(shù)據(jù)庫字段一致,該注解可省略
2.8 Mapper接口
package com.xiezhr.securityindbuser.mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}- 繼承
BaseMapper通用類,可以默認幫我們實現(xiàn)基本增刪改查
2.9 Service
① 接口
package com.xiezhr.securityindbuser.service;
public interface UserService extends IService<User> {
}② 實現(xiàn)
package com.xiezhr.securityindbuser.service.impl;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}2.10 Controller
package com.xiezhr.securityindbuser.controller;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/list")
public List<User> getUserList(){
return userService.list();
}
}2.11 測試是否正常獲取數(shù)據(jù)
以上小節(jié)中,我們建立了各種類。結(jié)構(gòu)如下

啟動服務(wù),瀏覽器中輸入:http://localhost:8080/user/list 看看是否能查出數(shù)據(jù)庫中用戶數(shù)據(jù)?

至此,我們已經(jīng)成功整合數(shù)據(jù)庫,并且從數(shù)據(jù)庫中查詢出了用戶信息。
接下來,我們要做的就是把認證流程從內(nèi)存中獲取用戶信息替換成我們自己實現(xiàn)從數(shù)據(jù)庫中查詢用戶信息。
三、基于數(shù)據(jù)庫的用戶認證
3.1 認證流程
通過之前基于內(nèi)存認證分析,我們知道。只要實現(xiàn)UserDetailsService 接口的loadUserByUsername 方法就可以從數(shù)據(jù)庫中獲取用戶信息。
- 程序啟動時:
- 創(chuàng)建
DBUserDetailsManager類,實現(xiàn)接口UserDetailsService接口 - 在應(yīng)用程序中初始化這個類的對象,使springsecurity不再從內(nèi)存中獲取用戶信息,而是通過我們自己實現(xiàn)類從數(shù)據(jù)庫中查詢用戶信息。
- 創(chuàng)建
- 校驗用戶時:
- SpringSecurity自動使用
DBUserDetailsManager的loadUserByUsername方法從數(shù)據(jù)庫中獲取User對象 - 在
UsernamePasswordAuthenticationFilter過濾器中的attemptAuthentication方法中將用戶輸入的用戶名密碼和從數(shù)據(jù)庫中獲取到的用戶信息進行比較,進行用戶認證
- SpringSecurity自動使用
3.2 創(chuàng)建DBUserDetailsManager
我們在service包下創(chuàng)建DBUserDetailsManager 來實現(xiàn)UserDetailsService 接口,替換從內(nèi)存中獲取用戶信息。代碼如下
package com.xiezhr.securityindbuser.service.impl;
public class DBUserDetailsManager implements UserDetailsService {
@Resource
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
//使用username構(gòu)造查詢條件
QueryWrapper<User> wrapper = queryWrapper.eq("username", username);
//由于用戶名不能重復(fù),所以我們使用selectOne查詢用戶信息即可
User user = userMapper.selectOne(wrapper);
if (user == null) {
//用戶不存在,拋出異常
throw new UsernameNotFoundException(username);
} else {
//由于現(xiàn)在還沒有權(quán)限信息,所以我們構(gòu)造一個空的權(quán)限信息
Collection< GrantedAuthority> authorities = new ArrayList<>();
return new org.springframework.security.core.userdetails.User(
user.getUsername(), //
user.getPassword(),
user.getEnabled(),
true, //用戶賬戶是否沒過期
true, //用戶憑證是否沒過期
true, //用戶是否未被鎖定
authorities //用戶權(quán)限信息
);
}
}
}3.3 初始化UserDetailsService
說了一堆理論,那么我們怎么才能讓springsecurity不從內(nèi)存獲取用戶信息,而是通過上一步創(chuàng)建的DBUserDetailsManager 來查詢用戶信息。
接下來的就比較關(guān)鍵了,我們只需創(chuàng)建一個WebSecurityConfig,然后創(chuàng)建基于數(shù)據(jù)庫的用戶管理器dbUserDetailsManager即可
package com.xiezhr.securityindbuser.config;
@Configuration //標明這個類為配置類,spring應(yīng)用程序一啟動,類中的been 就會被初始化在spring容器中
@EnableWebSecurity //開啟spring security 自定義配置
public class WebSecurityConfig {
@Bean
public UserDetailsService userDetailsService(){
//創(chuàng)建基于數(shù)據(jù)庫的用戶管理器
DBUserDetailsManager dbUserDetailsManager = new DBUserDetailsManager();
return dbUserDetailsManager;
}
}當然我們也可以直接在DBUserDetailsManager類上添加@Component注解,也能實現(xiàn)同樣的效果
3.4 測試一下
通過上面的步驟,基于數(shù)據(jù)庫的認證基本就完成了。
在整合數(shù)據(jù)庫的時候我們插入了三個用戶信息

下面我們來測試下成果,瀏覽器中輸入:http://localhost:8080/user/list

隨便輸入上面三個用戶中一個,admin/password xiezhr/password xiaofan/password 即可正常訪問接口

到此,我們成功完成了基于數(shù)據(jù)庫的用戶認證功能,是不是很簡單呢~
到此這篇關(guān)于SpringBoot3整合SpringSecurity6(三)基于數(shù)據(jù)庫的用戶認證的文章就介紹到這了,更多相關(guān)SpringBoot3整合SpringSecurity6(三)基于數(shù)據(jù)庫的用戶認證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot整合Springsecurity實現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制功能
- springboot版本升級以及解決springsecurity漏洞的問題
- SpringSecurity在SpringBoot中的自動裝配過程
- SpringBoot集成SpringSecurity安全框架方式
- SpringBoot+SpringSecurity實現(xiàn)認證的流程詳解
- SpringSecurity角色權(quán)限控制(SpringBoot+SpringSecurity+JWT)
- SpringBoot3.0+SpringSecurity6.0+JWT的實現(xiàn)
- SpringBoot淺析安全管理之基于數(shù)據(jù)庫認證
相關(guān)文章
JVM性能調(diào)優(yōu)之運行時參數(shù)小結(jié)
jvm是java的運行環(huán)境,在jvm中有很多的參數(shù)可以進行設(shè)置,本文主要介紹了JVM性能調(diào)優(yōu)之運行時參數(shù)小結(jié),具有一定的參考價值,感興趣的可以了解一下2024-04-04
解析ConcurrentHashMap: transfer方法源碼分析(難點)
ConcurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。Segment的結(jié)構(gòu)和HashMap類似,是一種數(shù)組和鏈表結(jié)構(gòu),今天給大家普及java面試常見問題---ConcurrentHashMap知識,一起看看吧2021-06-06
IDEA “Cannot resolve symbol”爆紅問題解決
最近發(fā)現(xiàn)個問題,IDEA 無法識別同一個 package 里的其他類,將其顯示為紅色,本文就來介紹一下IDEA “Cannot resolve symbol”爆紅問題解決,感興趣的可以了解一下2023-10-10
IDEA調(diào)試小技巧之Evaluate調(diào)試工具詳解
這篇文章主要介紹了IDEA調(diào)試小技巧之Evaluate調(diào)試工具,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09

