新聞中心
路徑攔截策略
在Spring Security中當然是按照不同的請求路徑規(guī)則定義專門的過濾器鏈,你可以通過三種方式來實現(xiàn)路徑攔截。然后按照策略定義過濾器鏈即可:

成都創(chuàng)新互聯(lián)主要業(yè)務有網(wǎng)站營銷策劃、成都網(wǎng)站設計、網(wǎng)站建設、微信公眾號開發(fā)、小程序定制開發(fā)、H5開發(fā)、程序開發(fā)等業(yè)務。一次合作終身朋友,是我們奉行的宗旨;我們不僅僅把客戶當客戶,還把客戶視為我們的合作伙伴,在開展業(yè)務的過程中,公司還積累了豐富的行業(yè)經(jīng)驗、全網(wǎng)整合營銷推廣資源和合作伙伴關系資源,并逐漸建立起規(guī)范的客戶服務和保障體系。
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityFilterChain systemSecurityFilterChain(HttpSecurity http) throws Exception {
// 省略
}
這三種策略介紹如下。
按照正則過濾
你可以通過HttpSecurity提供的過濾器過濾URI,例如攔截請求中在query參數(shù)而且包含id的URI:
http.regexMatcher("/(\\\\?|\\\\&)\" + id + \"=([^\\\\&]+)/") 這種常用來匹配一些帶參數(shù)的URL。
按照Ant規(guī)則過濾
這種是我們常見的方式,例如攔截/system開頭的所有路徑:
http.antMatcher("/system/**")關于這種方式這里不再贅述,詳細可以通過Ant規(guī)則詳解這一篇來了解。
按照RequestMatcher過濾
一些復雜的組合可以通過定義RequestMatcher接口來組合,例如這種復雜的規(guī)則:
RequestMatcher requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(
providerSettings.getTokenEndpoint(),
HttpMethod.POST.name()),
new AntPathRequestMatcher(
providerSettings.getTokenIntrospectionEndpoint(),
HttpMethod.POST.name()),
new AntPathRequestMatcher(
providerSettings.getTokenRevocationEndpoint(),
HttpMethod.POST.name()));
http.requestMatcher(requestMatcher)
滿足三個路徑中的一個就行,這種組合方式能夠實現(xiàn)最復雜的攔截策略。
配置隔離的一些要點
這里還要注意配置之間的隔離。
Session會話
默認情況下的Session依賴于cookie中設定的jsessionid, 如果你使用會話模式,必須隔離多個過濾器鏈的會話存儲,這樣能夠實現(xiàn)一個多個過濾器在同一個會話下不同的登錄狀態(tài),否則它們共享配置就會發(fā)生錯亂。
這是因為在一個會話下,默認的屬性Key是SPRING_SECURITY_CONTEXT,當在同一個會話下(同一個瀏覽器不同的tab頁)獲取當前上下文都是這樣的:
// 默認 SPRING_SECURITY_CONTEXT
Object contextFromSession = httpSession.getAttribute(this.springSecurityContextKey);
這樣登錄一個,其它都認為是登錄狀態(tài),這顯然不符合預期。你需要在不同的過濾器中定義不同的會話屬性Key。
final String ID_SERVER_SYSTEM_SECURITY_CONTEXT_KEY ="SOME_UNIQUE_KEY"
HttpSessionSecurityContextRepository hs = new HttpSessionSecurityContextRepository();
hs.setSpringSecurityContextKey(ID_SERVER_SYSTEM_SECURITY_CONTEXT_KEY);
http.securityContext().securityContextRepository(hs)
無狀態(tài)Token
無狀態(tài)Token相對簡單一些,前端根據(jù)路徑分開存儲即可,而且Token中應該包含校驗過濾器鏈的信息以方便后端校驗,避免Token混用。
UserDetailsService
如果你的不同端的用戶是獨立的,你需要實現(xiàn)不同的UserDetailsService,但是存在多個UserDetailsService的話,
一定不要將它們直接注冊到Spring IoC中!
一定不要將它們直接注冊到Spring IoC中!
一定不要將它們直接注冊到Spring IoC中!
如果你一定要注冊到Spring IoC,你需要定義獨立的接口,就像這樣:
@FunctionalInterface
public interface OAuth2UserDetailsService {
UserDetails loadOAuth2UserByUsername(String username) throws UsernameNotFoundException;
}
然后實現(xiàn)該接口再注入Spring IoC,每個過濾器鏈配置的時候就可以這樣寫:
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http,
OAuth2UserDetailsService oAuth2UserDetailsService) throws Exception {
http.userDetailsService(oAuth2UserDetailsService::loadOAuth2UserByUsername)
}
但是Spring IoC中必須有一個UserDetailsService,你得這樣寫:
@Bean
UserDetailsService notFoundUserDetailsService() {
return username -> {
throw new UsernameNotFoundException("用戶未找到");
};
}
為啥不可用,因為注入Spring IoC的UserDetailsService是一個兜底的實現(xiàn),如果你只有一個實現(xiàn),放入Spring IoC無可厚非,如果你想讓多個各自走各自的就必須這樣寫最安全,不然還有一個默認的InMemoryUserDetailsManager也會生效成為兜底的。
其它
其它配置按照各自的配置就行了,目前我還沒有發(fā)現(xiàn)有沖突的地方。上面所講的東西,在Id Server授權服務器中就是這樣實現(xiàn)授權服務器過濾、后臺管理用戶和前臺授權用戶三者之間隔離的:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class IdServerSecurityConfiguration {
private static final String CUSTOM_CONSENT_PAGE_URI = "/oauth2/consent";
private static final String SYSTEM_ANT_PATH = "/system/**";
/**
* The constant ID_SERVER_SYSTEM_SECURITY_CONTEXT_KEY.
*/
public static final String ID_SERVER_SYSTEM_SECURITY_CONTEXT_KEY = "ID_SERVER_SYSTEM_SECURITY_CONTEXT";
/**
* 授權服務器配置
*
* @author felord.cn
* @since 1.0.0
*/
@Configuration(proxyBeanMethods = false)
public static class AuthorizationServerConfiguration {
/**
* Authorization server 集成 優(yōu)先級要高一些
*
* @param http the http
* @return the security filter chain
* @throws Exception the exception
* @since 1.0.0
*/
@Bean("authorizationServerSecurityFilterChain")
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurerauthorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();
// 把自定義的授權確認URI加入配置
authorizationServerConfigurer.authorizationEndpoint(authorizationEndpointConfigurer ->
authorizationEndpointConfigurer.consentPage(CUSTOM_CONSENT_PAGE_URI));
RequestMatcher authorizationServerEndpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
// 攔截 授權服務器相關的請求端點
http.requestMatcher(authorizationServerEndpointsMatcher)
.authorizeRequests().anyRequest().authenticated()
.and()
// 忽略掉相關端點的csrf
.csrf(csrf -> csrf
.ignoringRequestMatchers(authorizationServerEndpointsMatcher))
.formLogin()
.and()
// 應用 授權服務器的配置
.apply(authorizationServerConfigurer);
return http.build();
}
/**
* 配置 OAuth2.0 provider元信息
*
* @param port the port
* @return the provider settings
* @since 1.0.0
*/
@Bean
public ProviderSettings providerSettings(@Value("${server.port}") Integer port) {
//TODO 配置化 生產(chǎn)應該使用域名
return ProviderSettings.builder().issuer("http://localhost:" + port).build();
}
}
/**
* 后臺安全配置.
*
* @author felord.cn
* @since 1.0.0
*/
@Configuration(proxyBeanMethods = false)
public static class SystemSecurityConfiguration {
/**
* 管理后臺以{@code /system}開頭
*
* @param http the http
* @return the security filter chain
* @throws Exception the exception
* @see AuthorizationServerConfiguration
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityFilterChain systemSecurityFilterChain(HttpSecurity http, UserInfoService userInfoService) throws Exception {
SimpleAuthenticationEntryPoint authenticationEntryPoint = new SimpleAuthenticationEntryPoint();
AuthenticationEntryPointFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(authenticationEntryPoint);
HttpSessionSecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();
securityContextRepository.setSpringSecurityContextKey(ID_SERVER_SYSTEM_SECURITY_CONTEXT_KEY);
http.antMatcher(SYSTEM_ANT_PATH).csrf().disable()
.headers().frameOptions().sameOrigin()
.and()
.securityContext().securityContextRepository(securityContextRepository)
.and()
.authorizeRequests().anyRequest().authenticated()
/* .and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)*/
.and()
.userDetailsService(userInfoService::findByUsername)
.formLogin().loginPage("/system/login").loginProcessingUrl("/system/login")
.successHandler(new RedirectLoginAuthenticationSuccessHandler("/system"))
.failureHandler(authenticationFailureHandler).permitAll();
return http.build();
}
}
/**
* 普通用戶訪問安全配置.
*
* @author felord.cn
* @since 1.0.0
*/
@Configuration(proxyBeanMethods = false)
public static class OAuth2SecurityConfiguration {
/**
* Default security filter chain security filter chain.
*
* @param http the http
* @param oAuth2UserDetailsService the oauth2 user details service
* @param securityFilterChain the security filter chain
* @return the security filter chain
* @throws Exception the exception
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http,
OAuth2UserDetailsService oAuth2UserDetailsService,
@Qualifier("authorizationServerSecurityFilterChain") SecurityFilterChain securityFilterChain) throws Exception {
DefaultSecurityFilterChain authorizationServerFilterChain = (DefaultSecurityFilterChain) securityFilterChain;
SimpleAuthenticationEntryPoint authenticationEntryPoint = new SimpleAuthenticationEntryPoint();
AuthenticationEntryPointFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(authenticationEntryPoint);
http.requestMatcher(new AndRequestMatcher(
new NegatedRequestMatcher(new AntPathRequestMatcher(SYSTEM_ANT_PATH)),
new NegatedRequestMatcher(authorizationServerFilterChain.getRequestMatcher())
)).authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
).csrf().disable()
.userDetailsService(oAuth2UserDetailsService::loadOAuth2UserByUsername)
.formLogin().loginPage("/login")
.successHandler(new RedirectLoginAuthenticationSuccessHandler())
.failureHandler(authenticationFailureHandler).permitAll()
.and()
.oauth2ResourceServer().jwt();
return http.build();
}
}
}
你可以通過https://github.com/NotFound403/id-server下載源碼進行改造學習,歡迎Star。
當前名稱:一套系統(tǒng)多套用戶安全體系該怎么辦
瀏覽路徑:http://www.5511xx.com/article/djcggse.html


咨詢
建站咨詢
