Java中的Spring Security配置過濾器
前言:
CaptchaAuthenticationFilter是通過模仿UsernamePasswordAuthenticationFilter實(shí)現(xiàn)的。同樣的道理,由于UsernamePasswordAuthenticationFilter的配置是由FormLoginConfigurer來完成的,應(yīng)該也能模仿一下FormLoginConfigurer,寫一個(gè)配置類CaptchaAuthenticationFilterConfigurer去配置CaptchaAuthenticationFilter。
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
// 省略
} AbstractAuthenticationFilterConfigurer
FormLoginConfigurer看起來有點(diǎn)復(fù)雜,不過繼承關(guān)系并不復(fù)雜,只繼承了AbstractAuthenticationFilterConfigurer。
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
extends AbstractHttpConfigurer<T, B> {
} 理論上我們模仿一下,也繼承一下這個(gè)類,但是你會(huì)發(fā)現(xiàn)這種方式行不通。因?yàn)?code>AbstractAuthenticationFilterConfigurer只能Spring Security內(nèi)部使用,不建議自定義。原因在于它最終向HttpSecurity添加過濾器使用的是HttpSecurity.addFilter(Filter)方法,這個(gè)方法只有內(nèi)置過濾器(參見FilterOrderRegistration)才能使用。了解了這個(gè)機(jī)制之后,我們只能往上再抽象一層,去改造其父類AbstractHttpConfigurer。
改造過程
AbstractAuthenticationFilterConfigurer<B,T,F>中的B是實(shí)際指的HttpSecurity,因此這個(gè)要保留;
T指的是它本身的實(shí)現(xiàn),我們配置CaptchaAuthenticationFilter不需要下沉一層到FormLoginConfigurer這個(gè)繼承級(jí)別,直接在AbstractAuthenticationFilterConfigurer這個(gè)繼承級(jí)別實(shí)現(xiàn)即可,因此T這里指的就是需要配置類本身,也不需要再抽象化,因此是不需要的;同樣的原因F也不需要,很明確是CaptchaAuthenticationFilter,不需要再泛化。這樣CaptchaAuthenticationFilter的配置類結(jié)構(gòu)可以這樣定義:
public class CaptchaAuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CaptchaAuthenticationFilterConfigurer<H>, H> {
// 不再泛化 具體化
private final CaptchaAuthenticationFilter authFilter;
// 特定的驗(yàn)證碼用戶服務(wù)
private CaptchaUserDetailsService captchaUserDetailsService;
// 驗(yàn)證碼處理服務(wù)
private CaptchaService captchaService;
// 保存認(rèn)證請(qǐng)求細(xì)節(jié)的策略
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
// 默認(rèn)使用保存請(qǐng)求認(rèn)證成功處理器
private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
// 認(rèn)證成功處理器
private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;
// 登錄認(rèn)證端點(diǎn)
private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
// 是否 自定義頁面
private boolean customLoginPage;
// 登錄頁面
private String loginPage;
// 登錄成功url
private String loginProcessingUrl;
// 認(rèn)證失敗處理器
private AuthenticationFailureHandler failureHandler;
// 認(rèn)證路徑是否放開
private boolean permitAll;
// 認(rèn)證失敗的url
private String failureUrl;
/**
* Creates a new instance with minimal defaults
*/
public CaptchaAuthenticationFilterConfigurer() {
setLoginPage("/login/captcha");
this.authFilter = new CaptchaAuthenticationFilter();
}
public CaptchaAuthenticationFilterConfigurer<H> formLoginDisabled() {
this.formLoginEnabled = false;
return this;
}
public CaptchaAuthenticationFilterConfigurer<H> captchaUserDetailsService(CaptchaUserDetailsService captchaUserDetailsService) {
this.captchaUserDetailsService = captchaUserDetailsService;
return this;
}
public CaptchaAuthenticationFilterConfigurer<H> captchaService(CaptchaService captchaService) {
this.captchaService = captchaService;
return this;
}
public CaptchaAuthenticationFilterConfigurer<H> usernameParameter(String usernameParameter) {
authFilter.setUsernameParameter(usernameParameter);
return this;
}
public CaptchaAuthenticationFilterConfigurer<H> captchaParameter(String captchaParameter) {
authFilter.setCaptchaParameter(captchaParameter);
return this;
}
public CaptchaAuthenticationFilterConfigurer<H> parametersConverter(Converter<HttpServletRequest, CaptchaAuthenticationToken> converter) {
authFilter.setConverter(converter);
return this;
}
@Override
public void init(H http) throws Exception {
updateAuthenticationDefaults();
updateAccessDefaults(http);
registerDefaultAuthenticationEntryPoint(http);
// 這里禁用默認(rèn)頁面過濾器 如果你想自定義登錄頁面 可以自行實(shí)現(xiàn) 可能和FormLogin沖突
// initDefaultLoginFilter(http);
// 把對(duì)應(yīng)的Provider也在init時(shí)寫入HttpSecurity
initProvider(http);
}
@Override
public void configure(H http) throws Exception {
//這里改為使用前插過濾器方法
http.addFilterBefore(filter, LogoutFilter.class);
}
// 其它方法 同AbstractAuthenticationFilterConfigurer
} 其實(shí)就是模仿AbstractAuthenticationFilterConfigurer及其實(shí)現(xiàn)類的風(fēng)格把用的配置項(xiàng)實(shí)現(xiàn)一邊。這里值得一提的是CaptchaService的配置也可以從Spring IoC中查找(參考getBeanOrNull方法,這個(gè)方法在Spring Security中隨處可見,建議借鑒),這樣更加靈活,既能從方法配置也能自動(dòng)注入。
private void initProvider(H http) {
ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
// 沒有配置CaptchaUserDetailsService就去Spring IoC獲取
if (captchaUserDetailsService == null) {
captchaUserDetailsService = getBeanOrNull(applicationContext, CaptchaUserDetailsService.class);
}
// 沒有配置CaptchaService就去Spring IoC獲取
if (captchaService == null) {
captchaService = getBeanOrNull(applicationContext, CaptchaService.class);
}
// 初始化 Provider
CaptchaAuthenticationProvider captchaAuthenticationProvider = this.postProcess(new CaptchaAuthenticationProvider(captchaUserDetailsService, captchaService));
// 會(huì)增加到ProviderManager的注冊(cè)列表中
http.authenticationProvider(captchaAuthenticationProvider);
}配置類效果
我們來看看CaptchaAuthenticationFilterConfigurer的配置效果:
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, UserDetailsService userDetailsService) throws Exception {
http.csrf().disable()
.authorizeRequests()
.mvcMatchers("/foo/**").access("hasAuthority('ROLE_USER')")
.anyRequest().authenticated()
.and()
// 所有的 AbstractHttpConfigurer 都可以通過apply方法加入HttpSecurity
.apply(new CaptchaAuthenticationFilterConfigurer<>())
// 配置驗(yàn)證碼處理服務(wù) 這里直接true 方便測(cè)試
.captchaService((phone, rawCode) -> true)
// 通過手機(jī)號(hào)去拿驗(yàn)證碼,這里為了方便直接寫死了,實(shí)際phone和username做個(gè)映射
.captchaUserDetailsService(phone -> userDetailsService.loadUserByUsername("felord"))
// 默認(rèn)認(rèn)證成功跳轉(zhuǎn)到/路徑 這里改造成把認(rèn)證信息直接返回json
.successHandler((request, response, authentication) -> {
// 這里把認(rèn)證信息以JSON形式返回
ServletServerHttpResponse servletServerHttpResponse = new ServletServerHttpResponse(response);
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.write(authentication, MediaType.APPLICATION_JSON,servletServerHttpResponse);
});
return http.build();
}是不是要優(yōu)雅很多,解決了你自己配置過濾器的很多疑難雜癥。學(xué)習(xí)一定要模仿,先模仿成功,然后再分析思考為什么會(huì)模仿成功,最后形成自己的創(chuàng)造力。千萬不要被一些陌生概念唬住,有些改造是不需要去深入了解細(xì)節(jié)的。
到此這篇關(guān)于Java中的Spring Security配置濾器的文章就介紹到這了,更多相關(guān)Spring Security過濾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解java WebSocket的實(shí)現(xiàn)以及Spring WebSocket
這篇文章主要介紹了詳解java WebSocket的實(shí)現(xiàn)以及Spring WebSocket ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-01-01
nacos配置注冊(cè)中心時(shí)指定命名空間不起作用的問題
這篇文章主要介紹了nacos配置注冊(cè)中心時(shí)指定命名空間不起作用的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。2022-01-01
SpringMVC用JsonSerialize日期轉(zhuǎn)換方法
下面小編就為大家?guī)硪黄猄pringMVC用JsonSerialize日期轉(zhuǎn)換方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起 小編過來看看吧2016-11-11
java中BeanUtils.copyProperties的用法(超詳細(xì))
本文介紹了BeanUtils.copyProperties()方法的使用,包括其功能、用法、注意事項(xiàng)和示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
MyBatis Generator的簡(jiǎn)單使用方法示例
這篇文章主要給大家介紹了關(guān)于MyBatis Generator的簡(jiǎn)單使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02

