SpringSecurity-7-自定義AuthenticationProvider實(shí)現(xiàn)圖形驗(yàn)證碼
上一章節(jié)我們介紹了如何使用過(guò)濾器(Filter)實(shí)現(xiàn)圖形驗(yàn)證咏窿,這是屬于Servlet層面抹估,比較簡(jiǎn)單容易理解。那么這次我們介紹SpringSecurity提供的另一種比較高端的實(shí)現(xiàn)圖形化驗(yàn)證碼净响,這就是AuthenticationProvider自定義認(rèn)證。
認(rèn)證流程
我們?cè)?/p>
- SpringSecurity認(rèn)證流程源碼解析中介紹了SpringSecurity的認(rèn)證流程
其中介紹了系統(tǒng)的用戶信息喳瓣,保存在SpringSecurity的主體(Principal)中馋贤。主體中包含了所有經(jīng)過(guò)驗(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;
????}
說(shuō)明:
- Authentication中包含主體權(quán)限列表犹芹,主體憑據(jù),主體的詳細(xì)信息鞠绰,及是否驗(yàn)證成功等腰埂。
- AuthenticationProvider被SpringSecurity定義為一個(gè)驗(yàn)證過(guò)程
- 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)限配置(過(guò)濾器鏈):
?????*?1绞灼、被攔截的資源
?????*?2利术、資源所對(duì)應(yīng)的角色權(quán)限
?????* 3、定義認(rèn)證方式:httpBasic 低矮、httpForm
?????*?4印叁、定制登錄頁(yè)面、登錄請(qǐng)求地址军掂、錯(cuò)誤處理方式
?????*?5轮蜕、自定義?spring?security?過(guò)濾器
?????*?@param?http
?????*?@throws?Exception?????*/
????@Override
????protected?void?configure(HttpSecurity?http)?throws?Exception?{
????????http.csrf().disable()?//禁用跨站csrf攻擊防御,后面的章節(jié)會(huì)專門(mén)講解
????????????????.formLogin()
????????????????.authenticationDetailsSource(imageCodeWebAuthenticationDetailsSource)
????????????????.loginPage("/login/page")//一旦用戶的請(qǐng)求沒(méi)有權(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()//不需要通過(guò)登錄驗(yàn)證就可以被訪問(wèn)的資源路徑
????????????????.anyRequest().authenticated();
????}
}
主要修改如圖
測(cè)試
我們使用瀏覽器瀏覽http://localhost:8888玛追,輸入錯(cuò)誤的驗(yàn)證碼税课,結(jié)果為
如果您覺(jué)得本文不錯(cuò),歡迎關(guān)注,點(diǎn)贊,收藏支持痊剖,您的關(guān)注是我堅(jiān)持的動(dòng)力韩玩!
原創(chuàng)不易击狮,轉(zhuǎn)載請(qǐng)注明出處佛析,感謝支持!如果本文對(duì)您有用彪蓬,歡迎轉(zhuǎn)發(fā)分享寸莫!