1. 引言
在本教程中,我們將學習如何通過使用兩個不同的 Spring Security <em http</em> 元素來配置 Spring Security,使其能夠同時與兩個不同的登錄頁面工作。
2. 配置 2 個 HTTP 元素
在我們需要一個用於應用程序管理員和一個用於普通用户分開的登錄頁面的情況中,我們可能需要兩個登錄頁面。
我們將配置 2 個 http 元素,這些元素將通過關聯的 URL 模式進行區分:
- /user* 用於需要普通用户身份驗證才能訪問的頁面
- /admin* 用於由管理員訪問的頁面
每個 http 元素將具有不同的登錄頁面和不同的登錄處理 URL。
為了配置兩個不同的 http 元素,讓我們創建兩個靜態類,這些類使用 @Configuration 標註。
它們將被放置在常規 @Configuration 類內部:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
...
}讓我們定義用於“ADMIN”用户組的 ConfigurerAdapter:
@Configuration
@Order(1)
public static class App1ConfigurationAdapter {
@Bean
public SecurityFilterChain filterChainApp1(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
http.securityMatcher("/admin*")
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry.requestMatchers(mvcMatcherBuilder.pattern("/admin*")).hasRole("ADMIN"))
// log in
.formLogin(httpSecurityFormLoginConfigurer ->
httpSecurityFormLoginConfigurer.loginPage("/loginAdmin")
.loginProcessingUrl("/admin_login")
.failureUrl("/loginAdmin?error=loginError")
.defaultSuccessUrl("/adminPage"))
// logout
.logout(httpSecurityLogoutConfigurer ->
httpSecurityLogoutConfigurer.logoutUrl("/admin_logout")
.logoutSuccessUrl("/protectedLinks")
.deleteCookies("JSESSIONID"))
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
httpSecurityExceptionHandlingConfigurer.accessDeniedPage("/403"))
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
}現在,我們來定義一下針對普通用户的 ConfigurerAdapter:
@Configuration
@Order(2)
public static class App2ConfigurationAdapter {
@Bean
public SecurityFilterChain filterChainApp2(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
http.securityMatcher("/user*")
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry.requestMatchers(mvcMatcherBuilder.pattern("/user*")).hasRole("USER"))
// log in
.formLogin(httpSecurityFormLoginConfigurer ->
httpSecurityFormLoginConfigurer.loginPage("/loginUser")
.loginProcessingUrl("/user_login")
.failureUrl("/loginUser?error=loginError")
.defaultSuccessUrl("/userPage"))
// logout
.logout(httpSecurityLogoutConfigurer ->
httpSecurityLogoutConfigurer.logoutUrl("/user_logout")
.logoutSuccessUrl("/protectedLinks")
.deleteCookies("JSESSIONID"))
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
httpSecurityExceptionHandlingConfigurer.accessDeniedPage("/403"))
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
}請注意,通過在每個靜態類上放置 @Order 註解,我們正在指定在 URL 請求時進行模式匹配時,兩個類將被考慮的順序。
兩個配置類不能具有相同的順序。
3. 自定義登錄頁面
我們將為每種類型的用户創建自定義登錄頁面。對於管理員用户,登錄表單將具有“user_login””操作,如在配置中定義的那樣:
<p>User login page</p>
<form name="f" action="user_login" method="POST">
<table>
<tr>
<td>User:</td>
<td><input type="text" name="username" value=""></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit" /></td>
</tr>
</table>
</form>管理員登錄頁面與其它頁面類似,但表單的動作將設置為 “admin_login”,以符合 Java 配置。
4. 身份驗證配置
現在我們需要配置應用程序的身份驗證。下面我們將探討兩種實現方法:一種使用通用的用户身份驗證源,另一種使用兩個獨立的身份驗證源。
4.1. 使用通用的用户身份驗證源
如果兩個登錄頁面共享用於身份驗證用户的通用源,則可以創建一個類型的 UserDetailsService 豆,該豆將處理身份驗證。
讓我們使用 InMemoryUserDetailsManager 演示此場景,該管理器定義了兩個用户——一個具有 “USER” 角色的用户,以及一個具有 “ADMIN” 角色的用户:
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User
.withUsername("user")
.password(encoder().encode("userPass"))
.roles("USER")
.build());
manager.createUser(User
.withUsername("admin")
.password(encoder().encode("adminPass"))
.roles("ADMIN")
.build());
return manager;
}
@Bean
public static PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}4.2. 使用兩個不同的用户身份驗證源
如果您的用户身份驗證源不同——一個用於管理員,另一個用於普通用户——您可以在每個靜態 <em @Configuration</em> 類中配置一個 <em AuthenticationManagerBuilder</em>>。 讓我們來看一個用於 <em “ADMIN”</em> 用户的身份驗證管理器示例:
@Configuration
@Order(1)
public static class App1ConfigurationAdapter {
@Bean
public UserDetailsService userDetailsServiceApp1() {
UserDetails user = User.withUsername("admin")
.password(encoder().encode("admin"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
}在本例中,來自上一節的 UserDetailsService Bean 將不再使用。
6. 結論
在本快速教程中,我們演示瞭如何在同一個 Spring Security 應用程序中實現兩種不同的登錄頁面。
運行應用程序時,您可以通過 /protectedLinks URI 訪問上述示例。