javascript
base64编码 springboot_Spring Boot 中如何实现 HTTP 认证?
松哥給最近連載的 Spring Security 系列也錄制了視頻教程,感興趣的小伙伴請戳這里->Spring Boot+Vue+微人事視頻教程(Spring Boot 第十章就是 Spring Security)。
HttpBasic 認證有一定的局限性與安全隱患,因此在實際項目中使用并不多,但是,有的時候為了測試方便,開啟 HttpBasic 認證能方便很多。
因此松哥今天還是來和大家簡單聊一聊 Spring Security 中的 HttpBasic 認證。
本文是 Spring Security 系列第 29 篇,閱讀前面文章有助于更好理解本文:
1.什么是 HttpBasic
Http Basic 認證是 Web 服務器和客戶端之間進行認證的一種方式,最初是在 HTTP1.0 規范(RFC 1945)中定義,后續的有關安全的信息可以在 HTTP 1.1 規范(RFC 2616)和 HTTP 認證規范(RFC 2617)中找到。
HttpBasic 最大的優勢在于使用非常簡單,沒有復雜的頁面交互,只需要在請求頭中攜帶相應的信息就可以認證成功,而且它是一種無狀態登錄,也就是 session 中并不會記錄用戶的登錄信息。
HttpBasic 最大的問題在于安全性,因為用戶名/密碼只是簡單的通過 Base64 編碼之后就開始傳送了,很容易被工具嗅探到,進而暴露用戶信息。
Spring Security 中既支持基本的 HttpBasic 認證,也支持 Http 摘要認證,Http 摘要認證是在 HttpBasic 認證的基礎上,提高了信息安全管理,但是代碼復雜度也提高了不少,所以 Http 摘要認證使用并不多。
這里,松哥將和大家分享 Spring Security 中的這兩種認證方式。
2.HttpBasic 認證
我們先來看實現,再來分析它的認證流程。
首先創建一個 Spring Boot 項目,引入 Web 和 Spring Security 依賴,如下:
接下來創建一個測試接口:
@RestControllerpublic?class?HelloController?{
????@GetMapping("/hello")
????public?String?hello()?{
????????return?"hello";
????}
}
再開啟 HttpBasic 認證:
@Configurationpublic?class?SecurityConfig?extends?WebSecurityConfigurerAdapter?{
????@Override
????protected?void?configure(HttpSecurity?http)?throws?Exception?{
????????http.authorizeRequests()
????????????????.anyRequest().authenticated()
????????????????.and()
????????????????.httpBasic();
????}
}
最后再在 application.properties 中配置基本的用戶信息,如下:
spring.security.user.password=123spring.security.user.name=javaboy
配置完成后,啟動項目,訪問 /hello 接口,此時瀏覽器中會有彈出框,讓我們輸入用戶名/密碼信息:
此時我們查看請求響應頭,如下:
可以看到,瀏覽器響應了 401,同時還攜帶了一個 WWW-Authenticate 響應頭,這個是用來描述認證形式的,如果我們使用的是 HttpBasic 認證,默認響應頭格式如圖所示。
接下來我們輸入用戶名密碼,點擊 Sign In 進行登錄,登錄成功后,就可以成功訪問到 /hello 接口了。
我們查看第二次的請求,如下:
大家可以看到,在請求頭中,多了一個 Authorization 字段,該字段的值為 Basic amF2YWJveToxMjM=,
amF2YWJveToxMjM= 是一個經過 Base64 編碼之后的字符串,我們將該字符串解碼之后發現,結果如下:
String?x?=?new?String(Base64.getDecoder().decode("amF2YWJveToxMjM="),?"UTF-8");解碼結果如下:
可以看到,這就是我們的用戶名密碼信息。用戶名/密碼只是經過簡單的 Base64 編碼之后就開始傳遞了,所以說,這種認證方式比較危險??。
我們再來稍微總結一下 HttpBasic 認證的流程:
大致的流程就是這樣。
3.Http 摘要認證
Http 摘要認證與 HttpBasic 認證基本兼容,但是要復雜很多,這個復雜不僅體現在代碼上,也體現在請求過程中。
Http 摘要認證最重要的改進是他不會在網絡上發送明文密碼。它的整個認證流程是這樣的:
同時,服務端返回的字段還有一個 qop,表示保護級別,auth 表示只進行身份驗證;auth-int 表示還要校驗內容。
nonce 是服務端生成的隨機字符串,這是一個經過 Base64 編碼的字符串,經過解碼我們發現,它是由過期時間和密鑰組成的。在以后的請求中 nonce 會原封不動的再發回給服務端。
可以看到,客戶端發送到服務端的數據比較多。
- nonce 就是服務端發來的隨機字符串。
- response 是生成的摘要信息。
- nc 表示請求此時,可以防止重放攻擊。
- cnonce 表示客戶端發送給服務端的隨機字符串。
這就是整個流程。
一言以蔽之,原本的用戶密碼被摘要信息代替了,為了安全,摘要信息會根據服務端返回的隨機字符串而發生變化,服務端根據用戶密碼,同樣算出密碼的摘要信息,再和客戶端傳來的摘要信息進行對比,沒問題的話,用戶就算認證成功了。當然,在此基礎上還加了一些過期限制、重放攻擊防范機制等。
好了,那這個在 Spring Security 代碼中該怎么實現呢?
@Configurationpublic?class?SecurityConfig?extends?WebSecurityConfigurerAdapter?{
????@Override
????protected?void?configure(HttpSecurity?http)?throws?Exception?{
????????http.authorizeRequests()
????????????????.anyRequest().authenticated()
????????????????.and()
????????????????.csrf()
????????????????.disable()
????????????????.exceptionHandling()
????????????????.authenticationEntryPoint(digestAuthenticationEntryPoint())
????????????????.and()
????????????????.addFilter(digestAuthenticationFilter());
????}
????@Bean
????DigestAuthenticationEntryPoint?digestAuthenticationEntryPoint()?{
????????DigestAuthenticationEntryPoint?entryPoint?=?new?DigestAuthenticationEntryPoint();
????????entryPoint.setKey("javaboy");
????????entryPoint.setRealmName("myrealm");
????????entryPoint.setNonceValiditySeconds(1000);
????????return?entryPoint;
????}
????@Bean
????DigestAuthenticationFilter?digestAuthenticationFilter()?{
????????DigestAuthenticationFilter?filter?=?new?DigestAuthenticationFilter();
????????filter.setAuthenticationEntryPoint(digestAuthenticationEntryPoint());
????????filter.setUserDetailsService(userDetailsService());
????????return?filter;
????}
????@Override
????@Bean
????protected?UserDetailsService?userDetailsService()?{
????????InMemoryUserDetailsManager?manager?=?new?InMemoryUserDetailsManager();
????????manager.createUser(User.withUsername("javaboy").password("123").roles("admin").build());
????????return?manager;
????}
????@Bean
????PasswordEncoder?passwordEncoder()?{
????????return?NoOpPasswordEncoder.getInstance();
????}
}
配置無非就是兩方面,一方面是服務端隨機字符串的生成,另一方面就是客戶端摘要信息的校驗。
??AuthenticationException?authException)?throws?IOException?{
?HttpServletResponse?httpResponse?=?response;
?long?expiryTime?=?System.currentTimeMillis()?+?(nonceValiditySeconds?*?1000);
?String?signatureValue?=?DigestAuthUtils.md5Hex(expiryTime?+?":"?+?key);
?String?nonceValue?=?expiryTime?+?":"?+?signatureValue;
?String?nonceValueBase64?=?new?String(Base64.getEncoder().encode(nonceValue.getBytes()));
?String?authenticateHeader?=?"Digest?realm=\""?+?realmName?+?"\",?"
???+?"qop=\"auth\",?nonce=\""?+?nonceValueBase64?+?"\"";
?if?(authException?instanceof?NonceExpiredException)?{
??authenticateHeader?=?authenticateHeader?+?",?stale=\"true\"";
?}
?if?(logger.isDebugEnabled())?{
??logger.debug("WWW-Authenticate?header?sent?to?user?agent:?"
????+?authenticateHeader);
?}
?httpResponse.addHeader("WWW-Authenticate",?authenticateHeader);
?httpResponse.sendError(HttpStatus.UNAUTHORIZED.value(),
??HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
在這段代碼中,首先獲取到過期時間,然后給過期時間和 key 一起計算出消息摘要,再將 nonce 和消息摘要共同作為 value,計算出一個 Base64 編碼字符,再將該編碼字符寫回到前端。
配置完成后,重啟服務端進行測試。
測試效果其實和 HttpBasic 認證是一樣的,所有的變化,只是背后的實現有所變化而已,用戶體驗是一樣的。
4.小結
Http 摘要認證的效果雖然比 HttpBasic 安全,但是其實大家看到,整個流程下來解決的安全問題其實還是非常有限。而且代碼也麻煩了很多,因此這種認證方式并未廣泛流行開來。
Http 認證小伙伴們作為一個了解即可,里邊的有一些思想還是挺有意思的,可以激發我們解決其他問題的思路,例如對于重放攻擊的的解決辦法,我們如果想自己防御重放攻擊,就可以參考這里的實現思路。
好啦,小伙伴們如果有收獲,記得點個在看鼓勵下松哥哦~
今日干貨
剛剛發表查看:66666回復:666公眾號后臺回復 ssm,免費獲取松哥純手敲的 SSM 框架學習干貨。
總結
以上是生活随笔為你收集整理的base64编码 springboot_Spring Boot 中如何实现 HTTP 认证?的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: python制作图片数据集_Pytorc
- 下一篇: sentry 命令_sentry(二)集
