Spring Security自定義身份認(rèn)證的實(shí)現(xiàn)方法
盡管項(xiàng)目啟動(dòng)時(shí),Spring Security會(huì)提供了默認(rèn)的用戶信息,可以快速認(rèn)證和啟動(dòng),但大多數(shù)應(yīng)用程序都希望使用自定義的用戶認(rèn)證。對(duì)于自定義用戶認(rèn)證,Spring Security提供了多種認(rèn)證方式,常用的有In-Memory Authentication(內(nèi)存身份認(rèn)證)、JDBC Authentication(JDBC身份認(rèn)證)和UserDetailsService(身份詳情服務(wù))。下面對(duì)Spring Security的這三種自定義身份認(rèn)證進(jìn)行詳細(xì)講解。
1.內(nèi)存身份認(rèn)證
以內(nèi)存身份認(rèn)證時(shí),需要在Spring Security的相關(guān)組件中進(jìn)行指定當(dāng)前認(rèn)證方式為內(nèi)存身份認(rèn)證。Spring Security 5.7.1開始Spring Security將WebSecurityConfigurerAdapter類標(biāo)注為過(guò)時(shí),推薦直接聲明配置類,在配置類中直接定義組件的信息。 本書使用Spring Boot 2.7.6,其對(duì)應(yīng)的Spring Security版本為5.7.5。自定義內(nèi)存身份認(rèn)證時(shí),可以通過(guò)InMemoryUserDetailsManager類實(shí)現(xiàn),InMemoryUserDetailsManager是UserDetailsService的一個(gè)實(shí)現(xiàn)類,方便在內(nèi)存中創(chuàng)建一個(gè)用戶。對(duì)此,只需 在自定義配置類中創(chuàng)建InMemoryUserDetailsManager實(shí)例,在該實(shí)例中指定該實(shí)例的認(rèn)證信息,并存入在Spring容器中即可。
(1)創(chuàng)建配置類
創(chuàng)建一個(gè)配置類WebSecurityConfig,在該類中創(chuàng)建UserDetailsService類型的InMemoryUserDetailsManager實(shí)例對(duì)象交由Spring容器管理
@Configuration
public class WebSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
users.createUser(User.withUsername("zhangsan")
.password("{noop}1234")
.roles("ADMIN")
.build());
return users;
}
}進(jìn)行自定義用戶認(rèn)證時(shí),需要注意以下幾個(gè)問(wèn)題。
提交認(rèn)證時(shí)會(huì)對(duì)輸入的密碼使用密碼編譯器進(jìn)行加密并與正確的密碼進(jìn)行校驗(yàn)。如果不想要對(duì)輸入的密碼進(jìn)行加密,需要在密碼前對(duì)使用{noop}進(jìn)行標(biāo)注。
從Spring Security 5開始,自定義用戶認(rèn)證如果沒(méi)有設(shè)置密碼編碼器,也沒(méi)有在密碼前使用{noop}進(jìn)行標(biāo)注,會(huì)認(rèn)證失敗。
自定義用戶認(rèn)證時(shí),可以定義用戶角色roles,也可以定義用戶權(quán)限authorities,在進(jìn)行賦值時(shí),權(quán)限通常是在角色值的基礎(chǔ)上添加“ROLE_”前綴。
自定義用戶認(rèn)證時(shí),可以為某個(gè)用戶一次指定多個(gè)角色或權(quán)限。
(2)驗(yàn)證內(nèi)存身份認(rèn)證
啟動(dòng)項(xiàng)目后,查看控制臺(tái)輸出的信息,發(fā)現(xiàn)沒(méi)有默認(rèn)安全管理時(shí)隨機(jī)生成了密碼。
在瀏覽器訪問(wèn)項(xiàng)目首頁(yè)“http://localhost:8080/”。

2.JDBC身份認(rèn)證
JDBC身份認(rèn)證是通過(guò)JDBC連接數(shù)據(jù)庫(kù),基于數(shù)據(jù)庫(kù)中已有的用戶信息進(jìn)行身份認(rèn)證,這樣避免了內(nèi)存身份認(rèn)證的弊端,可以實(shí)現(xiàn)對(duì)系統(tǒng)已注冊(cè)的用戶進(jìn)行身份認(rèn)證。JdbcUserDetailsManager是Spring Security內(nèi)置的UserDetailsService的實(shí)現(xiàn)類,使用JdbcUserDetailsManager可以通過(guò)JDBC將數(shù)據(jù)庫(kù)和Spring Security連接起來(lái)。下面對(duì)JDBC身份認(rèn)證方式進(jìn)行講解。
(1)數(shù)據(jù)準(zhǔn)備
使用之前創(chuàng)建的名為springbootdata的數(shù)據(jù)庫(kù),在該數(shù)據(jù)庫(kù)中創(chuàng)建三個(gè)表user、priv和user_priv,并預(yù)先插入幾條測(cè)試數(shù)據(jù)。準(zhǔn)備數(shù)據(jù)的SQL語(yǔ)句如下。
#選擇使用數(shù)據(jù)庫(kù)
USEspringbootdata;
#創(chuàng)建表user并插入相關(guān)數(shù)據(jù)
DROPTABLEIFEXISTS`user`;
CREATETABLE`user`(
`id`int(20)NOTNULLAUTO_INCREMENT,
`username`varchar(200)DEFAULTNULL,
`password`varchar(200)DEFAULTNULL,
`valid`tinyint(1)NOTNULLDEFAULT1,
PRIMARYKEY(`id`)
)ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf8;
INSERTINTO`user`VALUES('1','zhangsan',
'$2a$10$7fWqX7Y010pMnyym/AHZX.3chIbnPZbj3N/iqcG4APCF.hC6CMh5a','1');
INSERTINTO`user`VALUES('2','lisi',
'$2a$10$7fWqX7Y010pMnyym/AHZX.3chIbnPZbj3N/iqcG4APCF.hC6CMh5a','1');
#創(chuàng)建表priv并插入相關(guān)數(shù)據(jù)
DROPTABLEIFEXISTS`priv`;
CREATETABLE`priv`(
`id`int(20)NOTNULLAUTO_INCREMENT,
`authority`varchar(20)DEFAULTNULL,
PRIMARYKEY(`id`)
)ENGINE=InnoDBAUTO_INCREMENT=3DEFAULTCHARSET=utf8;
INSERTINTO`priv`VALUES('1','ROLE_COMMON');
INSERTINTO`priv`VALUES('2','ROLE_ADMIN');
#創(chuàng)建表user_priv并插入相關(guān)數(shù)據(jù)
DROPTABLEIFEXISTS`user_priv`;
CREATETABLE`user_priv`(
`id`int(20)NOTNULLAUTO_INCREMENT,
`user_id`int(20)DEFAULTNULL,
`priv_id`int(20)DEFAULTNULL,
PRIMARYKEY(`id`)
)ENGINE=InnoDBAUTO_INCREMENT=5DEFAULTCHARSET=utf8;
INSERTINTO`user_priv`VALUES('1','1','1');
INSERTINTO`user_priv`VALUES('2','2','2');(2)配置依賴
添加JDBC的啟動(dòng)器依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>(3)設(shè)置配置信息
設(shè)置數(shù)據(jù)庫(kù)連接的相關(guān)配置信息
(4)修改配置類
修改WebSecurityConfig配置類userDetailsService()方法,將該方法創(chuàng)建的實(shí)例對(duì)象修改為JdbcUserDetailsManager
@Configuration
public class WebSecurityConfig {
@Autowired
private DataSource dataSource;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
String userSQL ="SELECT username,password, valid " +
"FROM user WHERE username = ?";
String authoritySQL="SELECT u.username,p.authority " +
"FROM user u,priv p,user_priv up " +
"WHERE up.user_id=u.id AND up.priv_id=p.id and u.username =?";
JdbcUserDetailsManager users = new JdbcUserDetailsManager();
users.setDataSource(dataSource);
users.setUsersByUsernameQuery(userSQL);
users.setAuthoritiesByUsernameQuery(authoritySQL);
return users;
}
}(5)效果測(cè)試
重啟項(xiàng)目進(jìn)行效果測(cè)試
3.自定義UserDetailsService身份認(rèn)證
使用InMemoryUserDetailsManager和JdbcUserDetailsManager進(jìn)行身份認(rèn)證時(shí),其真正的認(rèn)證邏輯都在UserDetailsService接口重寫的loadUserByUsername()方法中。對(duì)于一個(gè)完善的項(xiàng)目來(lái)說(shuō),通常會(huì)實(shí)現(xiàn)用戶信息查詢服務(wù),對(duì)此可以自定義一個(gè)UserDetailsService實(shí)現(xiàn)類,重寫該接口的loadUserByUsername()方法,在該方法中查詢用戶信息,將查詢到的用戶信息填充到UserDetails對(duì)象返回,以實(shí)現(xiàn)用戶的身份認(rèn)證。下面通過(guò)案例對(duì)自定義UserDetailsService進(jìn)行身份驗(yàn)證的實(shí)現(xiàn)進(jìn)行演示 。
(1)創(chuàng)建實(shí)體類
在子包entity下創(chuàng)建用戶實(shí)體類UserDto和權(quán)限實(shí)體類Privilege
public class UserDto {
private Integer id; //用戶編號(hào)
private String username; //用戶名稱
private String password; //密碼
private Integer valid; //是否合法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this .id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this .username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this .password = password;
}
public Integer getValid() {
return valid;
}
public void setValid(Integer valid) {
this .valid = valid;
}
}public class Privilege {
private Integer id; //編號(hào)
private String authority; //權(quán)限
public Integer getId() {
return id;
}
public void setId(Integer id) {
this .id = id;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this .authority = authority;
}
}(2)創(chuàng)建用戶持久層接口
在dao子包下創(chuàng)建用戶持久層接口,在接口中定義查詢用戶及角色信息的方法
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
//根據(jù)賬號(hào)查詢用戶信息
public UserDto getUserByUsername(String username){
String sql = "SELECT * FROM user WHERE username = ?";
//連接數(shù)據(jù)庫(kù)查詢用戶
List<UserDto> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(UserDto.class ),username);
if (list !=null && list.size()==1){
return list.get(0);
}
return null;
}
//根據(jù)用戶id查詢用戶權(quán)限
public List<String> findPrivilegesByUserId(Integer userId){
String sql = "SELECT u.username,p.authority " +
"FROM user u,priv p,user_priv up " +
"WHERE up.user_id=u.id AND up.priv_id=p.id and u.id =?";
List<Privilege> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Privilege.class ), userId);
List<String> privileges = new ArrayList<>();
list.forEach(p -> privileges.add(p.getAuthority()));
return privileges;
}
}(3)封裝用戶認(rèn)證信息
在service子包下創(chuàng)建UserDetailsServiceImpl類,該類實(shí)現(xiàn)UserDetailsService接口,并在重寫的loadUserByUsername()方法中封裝用戶認(rèn)證信息
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserDao userDao;
//根據(jù)用戶名查詢用戶信息
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//連接數(shù)據(jù)庫(kù)根據(jù)賬號(hào)查詢用戶信息
UserDto userDto = userDao.getUserByUsername(username);
if (userDto == null){
//如果用戶查不到,返回null,會(huì)拋出異常
return null;
}
//根據(jù)用戶的id查詢用戶的權(quán)限
List<String> privileges = userDao.findPrivilegesByUserId(userDto.getId());
//將privileges轉(zhuǎn)成數(shù)組
String[] privilegeArray = new String[privileges.size()];
privileges.toArray(privilegeArray);
UserDetails userDetails = User.withUsername(userDto.getUsername()).password(userDto.getPassword()).authorities(privilegeArray).build();
return userDetails;
}
}(4)效果測(cè)試
將userDetailsService()方法進(jìn)行注釋,使用自定義UserDetailsService身份認(rèn)證。
重啟項(xiàng)目進(jìn)行效果測(cè)試。
到此這篇關(guān)于Spring Security自定義身份認(rèn)證的文章就介紹到這了,更多相關(guān)Spring Security自定義身份認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot中@ConfigurationProperties輕松管理應(yīng)用程序的配置信息詳解
通過(guò)@ConfigurationProperties注解,可以將外部配置文件中的屬性值注入到JavaBean中,簡(jiǎn)化了配置屬性的讀取和管理,這使得SpringBoot應(yīng)用程序中配置文件的屬性值可以映射到POJO類中,實(shí)現(xiàn)類型安全的屬性訪問(wèn),此方法避免了手動(dòng)讀取配置文件屬性的需要2024-10-10
Java8如何構(gòu)建一個(gè)Stream示例詳解
Java 8 是迄今為止在語(yǔ)義上改動(dòng)上最大的一個(gè)平臺(tái)。下面這篇文章主要給大家介紹了關(guān)于Java8如何構(gòu)建一個(gè)Stream的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04
java8 如何實(shí)現(xiàn)分組計(jì)算數(shù)量和計(jì)算總數(shù)
這篇文章主要介紹了java8 如何實(shí)現(xiàn)分組計(jì)算數(shù)量和計(jì)算總數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
IDEA在Maven項(xiàng)目中使用本地jar包的方法
我們?cè)谀玫脚f項(xiàng)目的時(shí)候,經(jīng)常會(huì)遇到一種情況,就是這個(gè)項(xiàng)目的maven中依賴了一個(gè)本地的jar包,這種情況就需要引入這個(gè)jar包,所以本文給大家介紹了IDEA在Maven項(xiàng)目中使用本地jar包的方法,需要的朋友可以參考下2024-04-04
SpringBoot解析指定Yaml配置文件的實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了SpringBoot解析指定Yaml配置文件,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
Java中String、StringBuffer和StringBuilder的區(qū)別與使用場(chǎng)景
在Java編程中,String、StringBuffer和StringBuilder是用于處理字符串的常見類,它們?cè)诳勺冃浴⒕€程安全性和性能方面有所不同,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
java arrayList遍歷的四種方法及Java中ArrayList類的用法
arraylist是動(dòng)態(tài)數(shù)組,它具有三個(gè)好處分別是:動(dòng)態(tài)的增加和減少元素 、實(shí)現(xiàn)了ICollection和IList接口、靈活的設(shè)置數(shù)組的大小,本文給大家介紹java arraylist遍歷及Java arraylist 用法,感興趣的朋友一起學(xué)習(xí)吧2015-11-11

