javascript
base64编码 springboot_Spring Boot 中如何实现 HTTP 认证?
松哥給最近連載的 Spring Security 系列也錄制了視頻教程,感興趣的小伙伴請(qǐng)戳這里->Spring Boot+Vue+微人事視頻教程(Spring Boot 第十章就是 Spring Security)。
HttpBasic 認(rèn)證有一定的局限性與安全隱患,因此在實(shí)際項(xiàng)目中使用并不多,但是,有的時(shí)候?yàn)榱藴y(cè)試方便,開(kāi)啟 HttpBasic 認(rèn)證能方便很多。
因此松哥今天還是來(lái)和大家簡(jiǎn)單聊一聊 Spring Security 中的 HttpBasic 認(rèn)證。
本文是 Spring Security 系列第 29 篇,閱讀前面文章有助于更好理解本文:
1.什么是 HttpBasic
Http Basic 認(rèn)證是 Web 服務(wù)器和客戶端之間進(jìn)行認(rèn)證的一種方式,最初是在 HTTP1.0 規(guī)范(RFC 1945)中定義,后續(xù)的有關(guān)安全的信息可以在 HTTP 1.1 規(guī)范(RFC 2616)和 HTTP 認(rèn)證規(guī)范(RFC 2617)中找到。
HttpBasic 最大的優(yōu)勢(shì)在于使用非常簡(jiǎn)單,沒(méi)有復(fù)雜的頁(yè)面交互,只需要在請(qǐng)求頭中攜帶相應(yīng)的信息就可以認(rèn)證成功,而且它是一種無(wú)狀態(tài)登錄,也就是 session 中并不會(huì)記錄用戶的登錄信息。
HttpBasic 最大的問(wèn)題在于安全性,因?yàn)橛脩裘?密碼只是簡(jiǎn)單的通過(guò) Base64 編碼之后就開(kāi)始傳送了,很容易被工具嗅探到,進(jìn)而暴露用戶信息。
Spring Security 中既支持基本的 HttpBasic 認(rèn)證,也支持 Http 摘要認(rèn)證,Http 摘要認(rèn)證是在 HttpBasic 認(rèn)證的基礎(chǔ)上,提高了信息安全管理,但是代碼復(fù)雜度也提高了不少,所以 Http 摘要認(rèn)證使用并不多。
這里,松哥將和大家分享 Spring Security 中的這兩種認(rèn)證方式。
2.HttpBasic 認(rèn)證
我們先來(lái)看實(shí)現(xiàn),再來(lái)分析它的認(rèn)證流程。
首先創(chuàng)建一個(gè) Spring Boot 項(xiàng)目,引入 Web 和 Spring Security 依賴(lài),如下:
接下來(lái)創(chuàng)建一個(gè)測(cè)試接口:
@RestControllerpublic?class?HelloController?{
????@GetMapping("/hello")
????public?String?hello()?{
????????return?"hello";
????}
}
再開(kāi)啟 HttpBasic 認(rèn)證:
@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
配置完成后,啟動(dòng)項(xiàng)目,訪問(wèn) /hello 接口,此時(shí)瀏覽器中會(huì)有彈出框,讓我們輸入用戶名/密碼信息:
此時(shí)我們查看請(qǐng)求響應(yīng)頭,如下:
可以看到,瀏覽器響應(yīng)了 401,同時(shí)還攜帶了一個(gè) WWW-Authenticate 響應(yīng)頭,這個(gè)是用來(lái)描述認(rèn)證形式的,如果我們使用的是 HttpBasic 認(rèn)證,默認(rèn)響應(yīng)頭格式如圖所示。
接下來(lái)我們輸入用戶名密碼,點(diǎn)擊 Sign In 進(jìn)行登錄,登錄成功后,就可以成功訪問(wèn)到 /hello 接口了。
我們查看第二次的請(qǐng)求,如下:
大家可以看到,在請(qǐng)求頭中,多了一個(gè) Authorization 字段,該字段的值為 Basic amF2YWJveToxMjM=,
amF2YWJveToxMjM= 是一個(gè)經(jīng)過(guò) Base64 編碼之后的字符串,我們將該字符串解碼之后發(fā)現(xiàn),結(jié)果如下:
String?x?=?new?String(Base64.getDecoder().decode("amF2YWJveToxMjM="),?"UTF-8");解碼結(jié)果如下:
可以看到,這就是我們的用戶名密碼信息。用戶名/密碼只是經(jīng)過(guò)簡(jiǎn)單的 Base64 編碼之后就開(kāi)始傳遞了,所以說(shuō),這種認(rèn)證方式比較危險(xiǎn)??。
我們?cè)賮?lái)稍微總結(jié)一下 HttpBasic 認(rèn)證的流程:
大致的流程就是這樣。
3.Http 摘要認(rèn)證
Http 摘要認(rèn)證與 HttpBasic 認(rèn)證基本兼容,但是要復(fù)雜很多,這個(gè)復(fù)雜不僅體現(xiàn)在代碼上,也體現(xiàn)在請(qǐng)求過(guò)程中。
Http 摘要認(rèn)證最重要的改進(jìn)是他不會(huì)在網(wǎng)絡(luò)上發(fā)送明文密碼。它的整個(gè)認(rèn)證流程是這樣的:
同時(shí),服務(wù)端返回的字段還有一個(gè) qop,表示保護(hù)級(jí)別,auth 表示只進(jìn)行身份驗(yàn)證;auth-int 表示還要校驗(yàn)內(nèi)容。
nonce 是服務(wù)端生成的隨機(jī)字符串,這是一個(gè)經(jīng)過(guò) Base64 編碼的字符串,經(jīng)過(guò)解碼我們發(fā)現(xiàn),它是由過(guò)期時(shí)間和密鑰組成的。在以后的請(qǐng)求中 nonce 會(huì)原封不動(dòng)的再發(fā)回給服務(wù)端。
可以看到,客戶端發(fā)送到服務(wù)端的數(shù)據(jù)比較多。
- nonce 就是服務(wù)端發(fā)來(lái)的隨機(jī)字符串。
- response 是生成的摘要信息。
- nc 表示請(qǐng)求此時(shí),可以防止重放攻擊。
- cnonce 表示客戶端發(fā)送給服務(wù)端的隨機(jī)字符串。
這就是整個(gè)流程。
一言以蔽之,原本的用戶密碼被摘要信息代替了,為了安全,摘要信息會(huì)根據(jù)服務(wù)端返回的隨機(jī)字符串而發(fā)生變化,服務(wù)端根據(jù)用戶密碼,同樣算出密碼的摘要信息,再和客戶端傳來(lái)的摘要信息進(jìn)行對(duì)比,沒(méi)問(wèn)題的話,用戶就算認(rèn)證成功了。當(dāng)然,在此基礎(chǔ)上還加了一些過(guò)期限制、重放攻擊防范機(jī)制等。
好了,那這個(gè)在 Spring Security 代碼中該怎么實(shí)現(xiàn)呢?
@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();
????}
}
配置無(wú)非就是兩方面,一方面是服務(wù)端隨機(jī)字符串的生成,另一方面就是客戶端摘要信息的校驗(yàn)。
??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());
}
在這段代碼中,首先獲取到過(guò)期時(shí)間,然后給過(guò)期時(shí)間和 key 一起計(jì)算出消息摘要,再將 nonce 和消息摘要共同作為 value,計(jì)算出一個(gè) Base64 編碼字符,再將該編碼字符寫(xiě)回到前端。
配置完成后,重啟服務(wù)端進(jìn)行測(cè)試。
測(cè)試效果其實(shí)和 HttpBasic 認(rèn)證是一樣的,所有的變化,只是背后的實(shí)現(xiàn)有所變化而已,用戶體驗(yàn)是一樣的。
4.小結(jié)
Http 摘要認(rèn)證的效果雖然比 HttpBasic 安全,但是其實(shí)大家看到,整個(gè)流程下來(lái)解決的安全問(wèn)題其實(shí)還是非常有限。而且代碼也麻煩了很多,因此這種認(rèn)證方式并未廣泛流行開(kāi)來(lái)。
Http 認(rèn)證小伙伴們作為一個(gè)了解即可,里邊的有一些思想還是挺有意思的,可以激發(fā)我們解決其他問(wèn)題的思路,例如對(duì)于重放攻擊的的解決辦法,我們?nèi)绻胱约悍烙胤殴?#xff0c;就可以參考這里的實(shí)現(xiàn)思路。
好啦,小伙伴們?nèi)绻惺斋@,記得點(diǎn)個(gè)在看鼓勵(lì)下松哥哦~
今日干貨
剛剛發(fā)表查看:66666回復(fù):666公眾號(hào)后臺(tái)回復(fù) ssm,免費(fèi)獲取松哥純手敲的 SSM 框架學(xué)習(xí)干貨。
總結(jié)
以上是生活随笔為你收集整理的base64编码 springboot_Spring Boot 中如何实现 HTTP 认证?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python制作图片数据集_Pytorc
- 下一篇: springboot获取payload_