SpringBoot配置Spring?Security的實(shí)現(xiàn)示例
1.繼承 WebSecurityConfigurerAdapter
通過(guò)重寫(xiě)抽象接口 WebSecurityConfigurerAdapter,再加上注解 @EnableWebSecurity,可以實(shí)現(xiàn) Web 的安全配置。
WebSecurityConfigurerAdapterConfig 模塊一共有 3 個(gè) builder(構(gòu)造程序)。
AuthenticationManagerBuilder:認(rèn)證相關(guān) builder,用來(lái)配置全局的認(rèn)證相關(guān)的信息。它包含AuthenticationProvider和UserDetailsService,前者是 認(rèn)證服務(wù)提供者,后者是 用戶(hù)詳情查詢(xún)服務(wù)。WebSecurity:進(jìn)行全局請(qǐng)求忽略規(guī)則配置、HttpFirewall 配置、debug 配置、全局 SecurityFilterChain 配置。HttpSecurity:進(jìn)行權(quán)限控制規(guī)則相關(guān)配置。
配置安全,通常要重寫(xiě)以下方法:
// 通過(guò) auth 對(duì)象的方法添加身份驗(yàn)證
protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
// 通常用于設(shè)置忽略權(quán)限的靜態(tài)資源
public void configure(WebSecurity web) throws Exception {}
// 通過(guò) HTTP 對(duì)象的 authorizeRequests() 方法定義 URL 訪問(wèn)權(quán)限。默認(rèn)為 formLogin() 提供一個(gè)簡(jiǎn)單的登錄驗(yàn)證頁(yè)面
protected void configure(HttpSecurity httpSecurity) throws Exception {}
2.配置自定義策略
配置安全需要繼承 WebSecurityConfigurerAdapter,然后重寫(xiě)其方法,見(jiàn)以下代碼:
package com.example.demo.config;
// 指定為配置類(lèi)
@Configuration
// 指定為 Spring Security 配置類(lèi),如果是 WebFlux,則需要啟用@EnableWebFluxSecurity
@EnableWebSecurity
// 如果要啟用方法安全設(shè)置,則開(kāi)啟此項(xiàng)。
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
//不攔截靜態(tài)資源
web.ignoring().antMatchers("/static/**");
}
@Bean
public PasswordEncoder passwordEncoder() {
// 使用 BCrypt 加密
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.usernameParameter("uname").passwordParameter("pwd").loginPage("/admin/login").permitAll()
.and()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
// 除上面外的所有請(qǐng)求全部需要鑒權(quán)認(rèn)證
.anyRequest().authenticated();
http.logout().permitAll();
http.rememberMe().rememberMeParameter("rememberme");
// 處理異常,拒絕訪問(wèn)就重定向到 403 頁(yè)面
http.exceptionHandling().accessDeniedPage("/403");
http.logout().logoutSuccessUrl("/");
http.csrf().ignoringAntMatchers("/admin/upload");
}
}
formLogin():自定義用戶(hù)登錄驗(yàn)證的頁(yè)面。表示開(kāi)啟表單登錄配置,loginPage用來(lái)配置登錄頁(yè)面地址;usernameParameter表示登錄用戶(hù)名的參數(shù)名稱(chēng);passwordParameter表示登錄密碼的參數(shù)名稱(chēng);permitAll表示跟登錄相關(guān)的頁(yè)面和接口不做攔截,直接通過(guò)。需要注意的是usernameParameter、passwordParameter需要和login.html中登錄表單的配置一致。authorizeRequests():定義哪些 URL 需要被保護(hù),哪些不需要被保護(hù)。antMatchers("/admin/**").hasRole("ADMIN"):定義/admin/下的所有 URL。只有擁有ADMIN角色的用戶(hù)才有訪問(wèn)權(quán)限。.logout().permitAll():表示在 Spring Security 配置中,無(wú)論用戶(hù)是否經(jīng)過(guò)身份驗(yàn)證,都可以訪問(wèn)注銷(xiāo)(logout)頁(yè)面。這意味著,即使是一個(gè)未經(jīng)過(guò)身份驗(yàn)證的用戶(hù)也可以訪問(wèn)注銷(xiāo)功能,這在某些情況下可能是一個(gè)安全隱患。因此,這個(gè)配置通常用于開(kāi)發(fā)環(huán)境或測(cè)試環(huán)境,以確保用戶(hù)可以輕松地測(cè)試注銷(xiāo)功能,而在生產(chǎn)環(huán)境中,出于安全考慮,通常會(huì)更加限制對(duì)注銷(xiāo)頁(yè)面的訪問(wèn)權(quán)限。http.csrf():配置是否開(kāi)啟 CSRF 保護(hù),還可以在開(kāi)啟之后指定忽略的接口。and():會(huì)返回 HttpSecurityBuilder 對(duì)象的一個(gè)子類(lèi)(實(shí)際上就是 HttpSecurity),所以and()方法相當(dāng)于又回到 HttpSecurity 實(shí)例,重新開(kāi)啟新一輪的配置。在 Spring Security 的配置中,.and()方法用于將多個(gè)安全配置連接起來(lái),形成一個(gè)連續(xù)的配置鏈。這種方法允許開(kāi)發(fā)者逐步添加和配置安全策略,確保每個(gè)配置步驟都是基于前一個(gè)步驟的結(jié)果進(jìn)行的。通過(guò)使用.and()方法,可以構(gòu)建復(fù)雜的安全配置,包括身份驗(yàn)證、授權(quán)、安全防護(hù)等。例如,在配置 Spring Security 時(shí),可能會(huì)先設(shè)置用戶(hù)認(rèn)證方式,然后使用.and()方法連接到授權(quán)配置,接著可能再次使用.and()方法連接到 CSRF 保護(hù)等高級(jí)配置。這種鏈?zhǔn)脚渲梅绞绞沟么a更加清晰和易于管理。
http
.someConfigurer
.<some feature of configurer>()
.<some feature of configurer>()
.and()
.someOtherConfigurer
.<some feature of someOtherConfigurer>()
...
.and()
...
如果開(kāi)啟了 CSRF,則一定在驗(yàn)證頁(yè)面加入以下代碼以傳遞 token 值:
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>
如果要提交表單,則需要在表單中添加以下代碼以提交 token 值:
<input type="hidden" th:name="${_csrf.parameterName)" th:value="${_csrf.token)">
http.rememberMe():“記住我” 功能,可以指定參數(shù)。
<input class="i-checks" type="checkbox" name="rememberme"/> 記住我
3.配置加密方式
默認(rèn)的加密方式是 BCrypt。只要在安全配置類(lèi)配置即可使用,見(jiàn)以下代碼:
@Bean
public PasswordEncoder passwordEncoder() {
// 使用 BCrypt 加密
return new BCryptPasswordEncoder();
}
在業(yè)務(wù)代碼中,可以用以下方式對(duì)密碼迸行加密:
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encodePassword = encoder.encode(password);
3.1 BCrypt 加密
BCrypt 是一種基于哈希函數(shù)的加密算法,它使用一個(gè) 密碼 和一個(gè) 鹽值 作為輸入,生成一個(gè)固定長(zhǎng)度的 密碼哈希值。這個(gè)哈希值在每次密碼輸入時(shí)都會(huì)重新生成,而且會(huì)隨著鹽值的改變而改變。BCrypt 的鹽值是一個(gè)隨機(jī)生成的字符串,與密碼一起用于哈希函數(shù)中,使得相同的密碼在每次加密時(shí)都會(huì)生成不同的哈希值。

BCrypt 的另一個(gè)重要特點(diǎn)是它使用了一個(gè)加密算法來(lái)混淆密碼哈希值。這個(gè)加密算法使用一個(gè)密鑰和一個(gè)初始化向量(IV)來(lái)加密密碼和鹽值。加密后的數(shù)據(jù)被存儲(chǔ)在數(shù)據(jù)庫(kù)中,用于后續(xù)的密碼驗(yàn)證。
BCrypt 的加密過(guò)程可以分為以下幾個(gè)步驟:
- 生成鹽值:BCrypt 使用一個(gè)隨機(jī)數(shù)生成器生成一個(gè)隨機(jī)的 鹽值。這個(gè)鹽值是一個(gè)隨機(jī)的字符串,用于與密碼一起生成哈希值。
- 混合鹽值和密碼:將 密碼 和 鹽值 混合在一起,然后使用一個(gè) 哈希函數(shù) 生成一個(gè)固定長(zhǎng)度的 哈希值。
- 加密哈希值:使用一個(gè) 加密算法 將哈希值混淆,生成一個(gè) 加密的哈希值。這個(gè)加密的哈希值被存儲(chǔ)在數(shù)據(jù)庫(kù)中。
- 驗(yàn)證密碼:在驗(yàn)證密碼時(shí),用戶(hù)輸入 密碼,系統(tǒng)使用相同的 鹽值、哈希函數(shù) 和 加密算法 生成一個(gè) 新的哈希值。然后,將新的哈希值與數(shù)據(jù)庫(kù)中的加密哈希值進(jìn)行比較,如果它們匹配,則密碼驗(yàn)證成功。

?? BCrypt 加密/匹配工具 - 在線密碼哈希生成與匹配

4.自定義加密規(guī)則
除默認(rèn)的加密規(guī)則,還可以自定義加密規(guī)則。具體見(jiàn)以下代碼:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception (
auth.userDetailsService(UserService()).passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return MD5Util.encode((String) charSequence);
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(MD5Util.encode((String) charSequence));
}
});
}
5.配置多用戶(hù)系統(tǒng)
一個(gè)完整的系統(tǒng)一般包含多種用戶(hù)系統(tǒng),比如 “后臺(tái)管理系統(tǒng) + 前端用戶(hù)系統(tǒng)"。Spring Security 默認(rèn)只提供一個(gè)用戶(hù)系統(tǒng),所以,需要通過(guò)配置以實(shí)現(xiàn)多用戶(hù)系統(tǒng)。
比如,如果要構(gòu)建一個(gè)前臺(tái)會(huì)員系統(tǒng),則可以通過(guò)以下步驟來(lái)實(shí)現(xiàn)。
5.1 構(gòu)建 UserDetailsService 用戶(hù)信息服務(wù)接口
構(gòu)建前端用戶(hù) UserSecurityService 類(lèi),并繼承 UserDetailsService。具體見(jiàn)以下代碼:
public class UserSecurityService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
User user = userRepository.findByName(name);
if (user == null) {
User mobileUser = userRepository.findByMobile(name);
if (mobileUser == null) {
User emailUser = userRepository .findByEmail(name);
if (emailUser == null) {
throw new UsernameNotFoundException("用戶(hù)名,郵箱或手機(jī)號(hào)不存在!");
} else {
user = userRepository.findByEmail(name);
}
} else {
user = userRepository.findByMobile(name);
}
} else if ("locked".equals(user.getStatus())) {
//被鎖定,無(wú)法登錄
throw new LockedException("用戶(hù)被鎖定”);
}
return user;
}
}
5.2 進(jìn)行安全配置
在繼承 WebSecurityConfigurerAdapter 的 Spring Security 配置類(lèi)中,配置 UserSecurityService 類(lèi)。
@Bean
UserDetailsService UserService() {
return new UserSecurityService();
}
如果要加入后臺(tái)管理系統(tǒng),則只需要重復(fù)上面步驟即可。
6.獲取當(dāng)前登錄用戶(hù)信息的幾種方式
獲取當(dāng)前登錄用戶(hù)的信息,在權(quán)限開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)遇到。而對(duì)新人來(lái)說(shuō),不太了解怎么獲取,經(jīng)常遇到獲取不到或報(bào)錯(cuò)的問(wèn)題。
所以,本節(jié)講解如何在常用地方獲取當(dāng)前用戶(hù)信息。
6.1 在 Thymeleaf 視圖中獲取
要 Thymeleaf 視圖中獲取用戶(hù)信息,可以使用 Spring Security 的標(biāo)簽特性。
在 Thymeleaf 頁(yè)面中引入 Thymeleaf 的 Spring Security 依賴(lài),見(jiàn)以下代碼:
<!DOCTYPE html>
<html lang="zh" xmlns:th=”http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<!-- 省略 -->
<body>
<!-- 匿名 -->
<div sec:authorize="isAnonymous()">
未登錄,單擊 <a th:href="@{/home/login}" rel="external nofollow" >登錄</a>
</div>
<!-- 已登錄 -->
<div sec:authorize="isAuthenticated()">
<p>已登錄</p>
<p>登錄名: <span sec:authentication="name"></span></p>
<p>角色: <span sec:authentication="principal.authorities"></span></p>
<p>id: <span sec:authentication="principal.id"></span></p>
<p>Username: <span sec:authentication="principal.username"></span></p>
</div>
</body>
</html>
這里要特別注意版本的對(duì)應(yīng)。如果引入了 thymeleaf-extras-springsecurity 依賴(lài)依然獲取不到信息,那么可能是 Thymeleaf 版本和 thymeleaf-extras-springsecurity 的版本不對(duì)。
請(qǐng)檢查在 pom.xml 文件的兩個(gè)依賴(lài),見(jiàn)以下代碼:
<dependency> <groupld>org.springframework.boot</groupld> <artifactld>spring-boot-starter-thymeleaf</artifactld> </dependency> <dependency> <groupld>org.thymeleaf.extras</groupld> <artifactld>thymeleaf-extras-springsecurity5</artifactld> </dependency>
6.2 在 Controller 中獲取
在控制器中獲取用戶(hù)信息有 3 種方式,見(jiàn)下面的代碼注釋。
@GetMapping("userinfo")
public String getProduct(Principal principal, Authentication authentication, HttpServletRequest httpServletRequest) {
/**
* Description: 1.通過(guò) Principal 參數(shù)獲取
*/
String username = principal.getName();
/**
* Description: 2.通過(guò) Authentication 參數(shù)獲取
*/
String userName2 = authentication.getName();
/**
* Description: 3.通過(guò) HttpServletRequest 獲取
*/
Principal httpServletRequestUserPrincipal = httpServletRequest.getUserPrincipal();
String userName3 = httpServletRequestUserPrincipal.getName();
return username;
}
6.3 在 Bean 中獲取
在 Bean 中,可以通過(guò)以下代碼獲取:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof AnonymousAuthenticationToken)) {
String username = authentication.getName();
return username;
}
在其他 Authentication 類(lèi)也可以這樣獲取。比如在 UsernamePasswordAuthenticationToken 類(lèi)中。
如果上面代碼獲取不到,并不是代碼錯(cuò)誤,則可能是因?yàn)橐韵略蛟斐傻模?/p>
- ? 要使上面的獲取生效,必須在繼承 WebSecurityConfigurerAdapter 的類(lèi)中的
http.antMatcher("/*")的鑒權(quán) URI 范圍內(nèi)。 - ? 沒(méi)有添加 Thymeleaf 的
thymeleaf-extras-springsecurity依賴(lài)。 - ? 添加了 Spring Security 的依賴(lài),但是版本不對(duì),比如 Spring Security 和 Thymeleaf 的版本不對(duì)。
到此這篇關(guān)于SpringBoot配置Spring Security的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringBoot配置Spring Security內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何用匿名內(nèi)部類(lèi)實(shí)現(xiàn) Java 同步回調(diào)
這篇文章主要介紹了如何用匿名內(nèi)部類(lèi)實(shí)現(xiàn) Java 同步回調(diào),幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下2020-10-10
IDEA編譯時(shí)報(bào)常量字符串過(guò)長(zhǎng)的解決辦法
本文主要介紹了IDEA編譯時(shí)報(bào)常量字符串過(guò)長(zhǎng)的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
解決Mybatis一對(duì)多/多對(duì)多查詢(xún)時(shí)只查出了一條數(shù)據(jù)問(wèn)題
在使用MyBatis進(jìn)行一對(duì)多查詢(xún)時(shí),若兩張表的ID字段名稱(chēng)相同,則會(huì)導(dǎo)致多的一方數(shù)據(jù)只查詢(xún)出來(lái)一條,解決方法是修改ID字段名稱(chēng),保持不一致,此經(jīng)驗(yàn)供參考,希望對(duì)大家有所幫助2026-05-05
Java繼承Thread類(lèi)創(chuàng)建線程類(lèi)示例
這篇文章主要介紹了Java繼承Thread類(lèi)創(chuàng)建線程類(lèi),結(jié)合實(shí)例形式分析了java線程操作相關(guān)使用技巧與注意事項(xiàng),需要的朋友可以參考下2019-09-09
MyBatis實(shí)現(xiàn)MySQL批量插入的示例代碼
本文主要介紹了MyBatis實(shí)現(xiàn)MySQL批量插入的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
SpringBoot結(jié)合Vue實(shí)現(xiàn)Python在線調(diào)試器
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何結(jié)合Vue實(shí)現(xiàn)Python在線調(diào)試器,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2026-01-01
java生成json實(shí)現(xiàn)隱藏掉關(guān)鍵屬性
這篇文章主要介紹了java生成json實(shí)現(xiàn)隱藏掉關(guān)鍵屬性,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA
這篇文章主要介紹了關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA的問(wèn)題,文中提供了詳細(xì)的圖文講解,需要的朋友可以參考下,如果有錯(cuò)誤的地方還請(qǐng)指正2023-03-03

