SpringSecurity-7-自定義AuthenticationProvider實(shí)現(xiàn)圖形驗(yàn)證碼

SpringSecurity-7-自定義AuthenticationProvider實(shí)現(xiàn)圖形驗(yàn)證碼

上一章節(jié)我們介紹了如何使用過濾器(Filter)實(shí)現(xiàn)圖形驗(yàn)證,這是屬于Servlet層面,比較簡(jiǎn)單容易理解。那么這次我們介紹SpringSecurity提供的另一種比較高端的實(shí)現(xiàn)圖形化驗(yàn)證碼,這就是AuthenticationProvider自定義認(rèn)證。

認(rèn)證流程

我們?cè)?/p>

其中介紹了系統(tǒng)的用戶信息,保存在SpringSecurity的主體(Principal)中。主體中包含了所有經(jīng)過驗(yàn)證用戶的權(quán)限,詳細(xì)信息等內(nèi)容。在SpringSecurity中將其封裝放在Authentication中,代碼如下

????public?interface?Authentication?extends?Principal,?Serializable?{
????????/**
?????????*?獲取用戶權(quán)限
?????????*?@return?????????*/
????????Collection<??extends?GrantedAuthority>?getAuthorities();
????????/**
?????????*?獲取用于的憑證,用戶密碼
?????????*?@return?????????*/
????????Object?getCredentials();
????????/**
?????????*?用戶的詳細(xì)信息
?????????*?@return?????????*/
????????Object?getDetails();
????????/**
?????????*?用戶憑證,一般為用戶名
?????????*?@return?????????*/
????????Object?getPrincipal();
????????/**
?????????*?用戶驗(yàn)證是否成功
?????????*?@return?????????*/
????????boolean?isAuthenticated();
????????void?setAuthenticated(boolean?isAuthenticated)?throws?IllegalArgumentException;
????}

說明:

  • Authentication中包含主體權(quán)限列表,主體憑據(jù),主體的詳細(xì)信息,及是否驗(yàn)證成功等。
  • AuthenticationProvider被SpringSecurity定義為一個(gè)驗(yàn)證過程
  • ProviderManager管理多個(gè)AuthenticationProvider

UsernamePasswordAuthenticationFilter

我們查看UsernamePasswordAuthenticationFilter類發(fā)現(xiàn)設(shè)置用戶信息的方法setDetails方法

從源碼我們可以看出authenticationDetailsSource是由AbstractAuthenticationProcessingFilter提供的AbstractAuthenticationProcessingFilter部分源碼如下

public?abstract?class?AbstractAuthenticationProcessingFilter?extends?GenericFilterBean
??????implements?ApplicationEventPublisherAware,?MessageSourceAware?{

???protected?ApplicationEventPublisher?eventPublisher;

???protected?AuthenticationDetailsSource<HttpServletRequest,??>?authenticationDetailsSource?=?new?WebAuthenticationDetailsSource();
?????...
???}

WebAuthenticationDetailsSource

在UsernamePasswordAuthenticationFilter中使用的AuthenticationDetailsSource是一個(gè)標(biāo)準(zhǔn)的Web認(rèn)證 源,攜帶的是用戶的sessionId和IP地址。源碼如圖所示

自定義WebAuthenticationDetails

有了HttpServletRequest之后,一切都將變得非常順暢。基于圖形驗(yàn)證碼的場(chǎng)景,我們可以繼承 WebAuthenticationDetails,并擴(kuò)展需要的信息。因此我們可以自定義WebAuthenticationDetails存儲(chǔ)額外信息。

/**
?*自定義WebAuthenticationDetails存儲(chǔ)額外的圖形驗(yàn)證信息?*/
public?class?ImageCodeWebAuthenticationDetails?extends?WebAuthenticationDetails?{
????/**
?????*?圖形信息是否驗(yàn)證成功?????*/
????private?boolean?imageCodeIsRight;

????public?boolean?getImageCodeIsRight(){
????????return?imageCodeIsRight;
????}
????public?ImageCodeWebAuthenticationDetails(HttpServletRequest?request)?{
????????super(request);
????????//?先獲取seesion中的驗(yàn)證碼
????????HttpSession?session?=?request.getSession();
????????String?sessionCode?=?(String)?session.getAttribute(CaptchaController.SESSION_KEY);
????????//?獲取用戶輸入的驗(yàn)證碼
????????String?inpuCode?=?request.getParameter("code");
????????if(!StringUtils.isEmpty(inpuCode)){
????????????//清除驗(yàn)證碼,不論驗(yàn)證成功還是失敗,都需要清除驗(yàn)證碼,并且在驗(yàn)證失敗的時(shí)候需要刷新驗(yàn)證碼
????????????session.removeAttribute("code");
????????????if(!StringUtils.isEmpty(sessionCode)&&?inpuCode.equalsIgnoreCase(sessionCode)?){
????????????????this.imageCodeIsRight=true;
????????????}
????????}

????}
}

自定義的AuthenticationDetailsSource。

@Component("imageCodeWebAuthenticationDetailsSource")
public?class?ImageCodeWebAuthenticationDetailsSource?implements?AuthenticationDetailsSource<HttpServletRequest,?WebAuthenticationDetails>?{
????@Override
????public?ImageCodeWebAuthenticationDetails?buildDetails(HttpServletRequest?context)?{
????????return?new?ImageCodeWebAuthenticationDetails(context);
????}
}

自定義AuthenticationProvider。

@Component("imageCodeAuthenticationProvider")
public?class?ImageCodeAuthenticationProvider?extends?DaoAuthenticationProvider?{

????public?ImageCodeAuthenticationProvider(UserDetailsService?userDetailsService,?PasswordEncoder?passwordEncoder)?{
????????this.setUserDetailsService(userDetailsService);
????????this.setPasswordEncoder(passwordEncoder);

????}

????@Override
????protected?void?additionalAuthenticationChecks(UserDetails?userDetails,?UsernamePasswordAuthenticationToken?authentication)?throws?AuthenticationException?{
????????//獲取詳細(xì)信息
????????ImageCodeWebAuthenticationDetails??details?=?(ImageCodeWebAuthenticationDetails)authentication.getDetails();
????????//如果驗(yàn)證碼不正確,拋出異常
????????if(!details.getImageCodeIsRight()){
????????????throw?new?ValidateCodeException("驗(yàn)證碼輸入錯(cuò)誤");
????????}
????????super.additionalAuthenticationChecks(userDetails,?authentication);
????}

}

修改配置類

想要應(yīng)用自定義的 AuthenticationProvider 和 AuthenticationDetailsSource,還需在LearnSrpingSecurity中完成剩余的配置。

/**
?*?安全配置類?*/
@EnableWebSecurity
public?class?LearnSrpingSecurity?extends?WebSecurityConfigurerAdapter?{

????@Autowired
????@Qualifier("imageCodeWebAuthenticationDetailsSource")
????private?AuthenticationDetailsSource<HttpServletRequest,WebAuthenticationDetails>?imageCodeWebAuthenticationDetailsSource;

????@Autowired
????@Qualifier("imageCodeAuthenticationProvider")
????private?AuthenticationProvider?imageCodeAuthenticationProvider;
????/**
?????*?認(rèn)證管理器
?????*?1.認(rèn)證信息提供方式(用戶名、密碼、當(dāng)前用戶的資源權(quán)限)
?????*?2.可采用內(nèi)存存儲(chǔ)方式,也可能采用數(shù)據(jù)庫(kù)方式等
?????*?@param?auth
?????*?@throws?Exception?????*/
????@Override
????protected?void?configure(AuthenticationManagerBuilder?auth)?throws?Exception?{
????????//super.configure(auth);
????????auth.authenticationProvider(imageCodeAuthenticationProvider);
????}
????/**
?????*?資源權(quán)限配置(過濾器鏈):
?????*?1、被攔截的資源
?????*?2、資源所對(duì)應(yīng)的角色權(quán)限
?????* 3、定義認(rèn)證方式:httpBasic 、httpForm
?????*?4、定制登錄頁(yè)面、登錄請(qǐng)求地址、錯(cuò)誤處理方式
?????*?5、自定義?spring?security?過濾器
?????*?@param?http
?????*?@throws?Exception?????*/
????@Override
????protected?void?configure(HttpSecurity?http)?throws?Exception?{

????????http.csrf().disable()?//禁用跨站csrf攻擊防御,后面的章節(jié)會(huì)專門講解
????????????????.formLogin()
????????????????.authenticationDetailsSource(imageCodeWebAuthenticationDetailsSource)
????????????????.loginPage("/login/page")//一旦用戶的請(qǐng)求沒有權(quán)限就跳轉(zhuǎn)到這個(gè)頁(yè)面
????????????????.loginProcessingUrl("/login/form")//登錄表單form中action的地址,也就是處理認(rèn)證請(qǐng)求的路徑
????????????????.usernameParameter("username")///登錄表單form中用戶名輸入框input的name名,不修改的話默認(rèn)是username
????????????????.passwordParameter("password")//form中密碼輸入框input的name名,不修改的話默認(rèn)是password
????????????????//.defaultSuccessUrl("/syslog")//登錄認(rèn)證成功后默認(rèn)轉(zhuǎn)跳的路徑
????????????????//.failureHandler(failureHandler)
????????????????.and()
????????????????.authorizeRequests()
????????????????.antMatchers("/login/page","/code/image").permitAll()//不需要通過登錄驗(yàn)證就可以被訪問的資源路徑
????????????????.anyRequest().authenticated();
????}
}

主要修改如圖

測(cè)試

我們使用瀏覽器瀏覽http://localhost:8888,輸入錯(cuò)誤的驗(yàn)證碼,結(jié)果為

如果您覺得本文不錯(cuò),歡迎關(guān)注,點(diǎn)贊,收藏支持,您的關(guān)注是我堅(jiān)持的動(dòng)力!

公眾號(hào)? springboot葵花寶典
主要分享JAVA技術(shù),主要包含SpringBoot、SpingCloud、Docker、中間件等技術(shù),以及Github開源項(xiàng)目

原創(chuàng)不易,轉(zhuǎn)載請(qǐng)注明出處,感謝支持!如果本文對(duì)您有用,歡迎轉(zhuǎn)發(fā)分享!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容