SpringBoot配置Spring?Security的實(shí)現(xiàn)示例
1.繼承 WebSecurityConfigurerAdapter
通過重寫抽象接口 WebSecurityConfigurerAdapter,再加上注解 @EnableWebSecurity,可以實(shí)現(xiàn) Web 的安全配置。
WebSecurityConfigurerAdapterConfig 模塊一共有 3 個(gè) builder(構(gòu)造程序)。
AuthenticationManagerBuilder
:認(rèn)證相關(guān) builder,用來配置全局的認(rèn)證相關(guān)的信息。它包含AuthenticationProvider
和UserDetailsService
,前者是 認(rèn)證服務(wù)提供者,后者是 用戶詳情查詢服務(wù)。WebSecurity
:進(jìn)行全局請(qǐng)求忽略規(guī)則配置、HttpFirewall 配置、debug 配置、全局 SecurityFilterChain 配置。HttpSecurity
:進(jìn)行權(quán)限控制規(guī)則相關(guān)配置。
配置安全,通常要重寫以下方法:
// 通過 auth 對(duì)象的方法添加身份驗(yàn)證 protected void configure(AuthenticationManagerBuilder auth) throws Exception {} // 通常用于設(shè)置忽略權(quán)限的靜態(tài)資源 public void configure(WebSecurity web) throws Exception {} // 通過 HTTP 對(duì)象的 authorizeRequests() 方法定義 URL 訪問權(quán)限。默認(rèn)為 formLogin() 提供一個(gè)簡(jiǎn)單的登錄驗(yàn)證頁(yè)面 protected void configure(HttpSecurity httpSecurity) throws Exception {}
2.配置自定義策略
配置安全需要繼承 WebSecurityConfigurerAdapter,然后重寫其方法,見以下代碼:
package com.example.demo.config; // 指定為配置類 @Configuration // 指定為 Spring Security 配置類,如果是 WebFlux,則需要啟用@EnableWebFluxSecurity @EnableWebSecurity // 如果要啟用方法安全設(shè)置,則開啟此項(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"); // 處理異常,拒絕訪問就重定向到 403 頁(yè)面 http.exceptionHandling().accessDeniedPage("/403"); http.logout().logoutSuccessUrl("/"); http.csrf().ignoringAntMatchers("/admin/upload"); } }
formLogin()
:自定義用戶登錄驗(yàn)證的頁(yè)面。表示開啟表單登錄配置,loginPage
用來配置登錄頁(yè)面地址;usernameParameter
表示登錄用戶名的參數(shù)名稱;passwordParameter
表示登錄密碼的參數(shù)名稱;permitAll
表示跟登錄相關(guān)的頁(yè)面和接口不做攔截,直接通過。需要注意的是usernameParameter
、passwordParameter
需要和login.html
中登錄表單的配置一致。authorizeRequests()
:定義哪些 URL 需要被保護(hù),哪些不需要被保護(hù)。antMatchers("/admin/**").hasRole("ADMIN")
:定義/admin/
下的所有 URL。只有擁有ADMIN
角色的用戶才有訪問權(quán)限。.logout().permitAll()
:表示在 Spring Security 配置中,無(wú)論用戶是否經(jīng)過身份驗(yàn)證,都可以訪問注銷(logout
)頁(yè)面。這意味著,即使是一個(gè)未經(jīng)過身份驗(yàn)證的用戶也可以訪問注銷功能,這在某些情況下可能是一個(gè)安全隱患。因此,這個(gè)配置通常用于開發(fā)環(huán)境或測(cè)試環(huán)境,以確保用戶可以輕松地測(cè)試注銷功能,而在生產(chǎn)環(huán)境中,出于安全考慮,通常會(huì)更加限制對(duì)注銷頁(yè)面的訪問權(quán)限。http.csrf()
:配置是否開啟 CSRF 保護(hù),還可以在開啟之后指定忽略的接口。and()
:會(huì)返回 HttpSecurityBuilder 對(duì)象的一個(gè)子類(實(shí)際上就是 HttpSecurity),所以and()
方法相當(dāng)于又回到 HttpSecurity 實(shí)例,重新開啟新一輪的配置。在 Spring Security 的配置中,.and()
方法用于將多個(gè)安全配置連接起來,形成一個(gè)連續(xù)的配置鏈。這種方法允許開發(fā)者逐步添加和配置安全策略,確保每個(gè)配置步驟都是基于前一個(gè)步驟的結(jié)果進(jìn)行的。通過使用.and()
方法,可以構(gòu)建復(fù)雜的安全配置,包括身份驗(yàn)證、授權(quán)、安全防護(hù)等。例如,在配置 Spring Security 時(shí),可能會(huì)先設(shè)置用戶認(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() ...
如果開啟了 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。只要在安全配置類配置即可使用,見以下代碼:
@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è)加密算法來混淆密碼哈希值。這個(gè)加密算法使用一個(gè)密鑰和一個(gè)初始化向量(IV
)來加密密碼和鹽值。加密后的數(shù)據(jù)被存儲(chǔ)在數(shù)據(jù)庫(kù)中,用于后續(xù)的密碼驗(yàn)證。
BCrypt 的加密過程可以分為以下幾個(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í),用戶輸入 密碼,系統(tǒng)使用相同的 鹽值、哈希函數(shù) 和 加密算法 生成一個(gè) 新的哈希值。然后,將新的哈希值與數(shù)據(jù)庫(kù)中的加密哈希值進(jìn)行比較,如果它們匹配,則密碼驗(yàn)證成功。
?? BCrypt 加密/匹配工具 - 在線密碼哈希生成與匹配
4.自定義加密規(guī)則
除默認(rèn)的加密規(guī)則,還可以自定義加密規(guī)則。具體見以下代碼:
@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.配置多用戶系統(tǒng)
一個(gè)完整的系統(tǒng)一般包含多種用戶系統(tǒng),比如 “后臺(tái)管理系統(tǒng) + 前端用戶系統(tǒng)"。Spring Security 默認(rèn)只提供一個(gè)用戶系統(tǒng),所以,需要通過配置以實(shí)現(xiàn)多用戶系統(tǒng)。
比如,如果要構(gòu)建一個(gè)前臺(tái)會(huì)員系統(tǒng),則可以通過以下步驟來實(shí)現(xiàn)。
5.1 構(gòu)建 UserDetailsService 用戶信息服務(wù)接口
構(gòu)建前端用戶 UserSecurityService 類,并繼承 UserDetailsService。具體見以下代碼:
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("用戶名,郵箱或手機(jī)號(hào)不存在!"); } else { user = userRepository.findByEmail(name); } } else { user = userRepository.findByMobile(name); } } else if ("locked".equals(user.getStatus())) { //被鎖定,無(wú)法登錄 throw new LockedException("用戶被鎖定”); } return user; } }
5.2 進(jìn)行安全配置
在繼承 WebSecurityConfigurerAdapter 的 Spring Security 配置類中,配置 UserSecurityService 類。
@Bean UserDetailsService UserService() { return new UserSecurityService(); }
如果要加入后臺(tái)管理系統(tǒng),則只需要重復(fù)上面步驟即可。
6.獲取當(dāng)前登錄用戶信息的幾種方式
獲取當(dāng)前登錄用戶的信息,在權(quán)限開發(fā)過程中經(jīng)常會(huì)遇到。而對(duì)新人來說,不太了解怎么獲取,經(jīng)常遇到獲取不到或報(bào)錯(cuò)的問題。
所以,本節(jié)講解如何在常用地方獲取當(dāng)前用戶信息。
6.1 在 Thymeleaf 視圖中獲取
要 Thymeleaf 視圖中獲取用戶信息,可以使用 Spring Security 的標(biāo)簽特性。
在 Thymeleaf 頁(yè)面中引入 Thymeleaf 的 Spring Security 依賴,見以下代碼:
<!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
依賴依然獲取不到信息,那么可能是 Thymeleaf 版本和 thymeleaf-extras-springsecurity
的版本不對(duì)。
請(qǐng)檢查在 pom.xml
文件的兩個(gè)依賴,見以下代碼:
<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 中獲取
在控制器中獲取用戶信息有 3 種方式,見下面的代碼注釋。
@GetMapping("userinfo") public String getProduct(Principal principal, Authentication authentication, HttpServletRequest httpServletRequest) { /** * Description: 1.通過 Principal 參數(shù)獲取 */ String username = principal.getName(); /** * Description: 2.通過 Authentication 參數(shù)獲取 */ String userName2 = authentication.getName(); /** * Description: 3.通過 HttpServletRequest 獲取 */ Principal httpServletRequestUserPrincipal = httpServletRequest.getUserPrincipal(); String userName3 = httpServletRequestUserPrincipal.getName(); return username; }
6.3 在 Bean 中獲取
在 Bean 中,可以通過以下代碼獲?。?/p>
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (!(authentication instanceof AnonymousAuthenticationToken)) { String username = authentication.getName(); return username; }
在其他 Authentication 類也可以這樣獲取。比如在 UsernamePasswordAuthenticationToken 類中。
如果上面代碼獲取不到,并不是代碼錯(cuò)誤,則可能是因?yàn)橐韵略蛟斐傻模?/p>
- ? 要使上面的獲取生效,必須在繼承 WebSecurityConfigurerAdapter 的類中的
http.antMatcher("/*")
的鑒權(quán) URI 范圍內(nèi)。 - ? 沒有添加 Thymeleaf 的
thymeleaf-extras-springsecurity
依賴。 - ? 添加了 Spring Security 的依賴,但是版本不對(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)文章
springboot集成mybatisPlus+多數(shù)據(jù)源的實(shí)現(xiàn)示例
這篇文章主要介紹了springboot集成mybatisPlus+多數(shù)據(jù)源的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12一次線上websocket返回400問題排查的實(shí)戰(zhàn)記錄
最近項(xiàng)目中有端對(duì)端通信場(chǎng)景,實(shí)時(shí)性要求較高,考慮后選用了websocket 這一通信協(xié)議,下面這篇文章主要給大家介紹了一次線上websocket返回400問題排查的實(shí)戰(zhàn)記錄,需要的朋友可以參考下2022-04-04springmvc實(shí)現(xiàn)導(dǎo)出數(shù)據(jù)信息為excle表格示例代碼
本篇文章主要介紹了springmvc實(shí)現(xiàn)導(dǎo)出數(shù)據(jù)信息為excle表格,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧。2017-01-01Java設(shè)計(jì)者模式簡(jiǎn)單工廠模式解析
這篇文章主要介紹了Java設(shè)計(jì)者模式簡(jiǎn)單工廠模式解析,介紹了其簡(jiǎn)介,實(shí)例以及優(yōu)缺點(diǎn)分析,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Java代碼中與Lua相互調(diào)用實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Java代碼中與Lua相互調(diào)用實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Java如何將json字符串與實(shí)體類互相轉(zhuǎn)換
在我們調(diào)用三方平臺(tái)接口時(shí),經(jīng)常需要將我們封裝的實(shí)體類轉(zhuǎn)換為json作為傳參,下面這篇文章主要給大家介紹了關(guān)于Java如何將json字符串與實(shí)體類互相轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2023-11-11Elasticsearch查詢之Term?Query示例解析
這篇文章主要為大家介紹了Elasticsearch查詢之Term?Query示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04