1. 簡介
本教程將重點介紹使用 Spring Security 進行登錄。我們將在此之前的 Spring MVC 示例基礎上進行擴展,因為 Spring MVC 及其登錄機制是搭建 Web 應用程序的必要組成部分。
2. Maven 依賴項
在使用 Spring Boot 時,spring-boot-starter-security 啓動器將自動包含所有依賴項,例如 spring-security-core、spring-security-web 和 spring-security-config 等:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
如果未使用 Spring Boot,請參閲 Spring Security with Maven 文章,其中描述瞭如何添加所有必需的依賴項。 spring-security-web 和 spring-security-config 均需要使用。
3. Spring Security Java Configuration
@Configuration
@EnableWebSecurity
public class SecSecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
// InMemoryUserDetailsManager (see below)
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// http builder configurations for authorize requests and form login (see below)
}
}
3.1. InMemoryUserDetailsManager
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user1 = User.withUsername("user1")
.password(passwordEncoder().encode("user1Pass"))
.roles("USER")
.build();
UserDetails user2 = User.withUsername("user2")
.password(passwordEncoder().encode("user2Pass"))
.roles("USER")
.build();
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("adminPass"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user1, user2, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
3.2. Configuration to Authorize Requests
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.antMatchers("/anonymous*")
.anonymous()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
// ...
}
3.3. Configuration for Form Login
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/homepage.html", true)
.failureUrl("/login.html?error=true")
.failureHandler(authenticationFailureHandler())
.and()
.logout()
.logoutUrl("/perform_logout")
.deleteCookies("JSESSIONID")
.logoutSuccessHandler(logoutSuccessHandler());
return http.build();
}
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
- ">
4. 將 Spring Security 添加到 Web 應用程序
為了使用上述定義的 Spring Security 配置,我們需要將其附加到 Web 應用程序。
我們將使用 WebApplicationInitializer,因此我們不需要提供 web.xml。
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext sc) {
AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.register(SecSecurityConfig.class);
sc.addListener(new ContextLoaderListener(root));
sc.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
}
}
注意,如果我們在使用 Spring Boot 應用程序,則不需要此初始化器。有關在 Spring Boot 中加載安全配置的更多信息,請參閲我們關於 Spring Boot 安全自動配置的文章。
5. Spring Security XML 配置
我們也將查看相應的 XML 配置。
整個項目使用 Java 配置,因此我們需要通過 Java @Configuration 類導入 XML 配置文件:
@Configuration
@ImportResource({ "classpath:webSecurityConfig.xml" })
public class SecSecurityConfig {
public SecSecurityConfig() {
super();
}
}
以及 Spring Security XML 配置,webSecurityConfig.xml:
<http use-expressions="true">
<intercept-url pattern="/login*" access="isAnonymous()" />
<intercept-url pattern="/**" access="isAuthenticated()"/>
<form-login login-page='/login.html'
default-target-url="/homepage.html"
authentication-failure-url="/login.html?error=true" />
<logout logout-success-url="/login.html" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="user1" password="user1Pass" authorities="ROLE_USER" />
</user-service>
<password-encoder ref="encoder" />
</authentication-provider>
</authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</beans:bean>
6. Web.xml 的配置
在 Spring 4 之前,我們使用在 web.xml 中添加額外的過濾器來配置 Spring Security:
<display-name>Spring Secured Application</display-name>
<!-- Spring MVC -->
<!-- ... -->
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
過濾器 – DelegatingFilterProxy – 僅僅委託給一個 Spring 管理的 Bean – 即 FilterChainProxy – 該 Bean 能夠充分利用 Spring 的 Bean 生命週期管理等功能。
7. 登錄表單
登錄表單頁面將使用 Spring MVC 的簡單機制註冊視圖名稱到 URL。 此外,不需要在視圖和控制器之間進行顯式映射:
registry.addViewController("/login.html");
這對應於 login.jsp
<html>
<head></head>
<body>
<h1>登錄</h1>
<form name='f' action="login" method='POST'>
<table>
<tr>
<td>用户名:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="提交" /></td>
</tr>
</table>
</form>
</body>
</html>
Spring 登錄表單
- login – 表單通過 POST 請求提交到的 URL,以觸發身份驗證過程
- username – 用户名
- password – 密碼
8. Further Configuring Spring Login
我們簡要討論了在介紹 Spring Security 配置時登錄機制的幾個配置。現在讓我們更詳細地瞭解一下。覆蓋 Spring Security 的大多數默認值的一個原因是隱藏應用程序使用 Spring Security 的事實。我們還希望儘量減少潛在攻擊者對應用程序所瞭解的信息。
完全配置後,登錄元素如下所示:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/homepage.html",true)
.failureUrl("/login.html?error=true")
return http.build();
}
或者對應的 XML 配置:
<form-login
login-page='/login.html'
login-processing-url="/perform_login"
default-target-url="/homepage.html"
authentication-failure-url="/login.html?error=true"
always-use-default-target="true"/>
8.1. The Login Page
接下來,我們將使用 loginPage() 方法 配置一個自定義登錄頁面:http.formLogin()
.loginPage("/login.html")
同樣,我們可以使用 XML 配置:
login-page='/login.html'
如果未指定此項,Spring Security 將生成一個基本的 Login Form 在 /login URL。
8.2. The POST URL for Login
Spring Login 將 POST 到觸發身份驗證過程的默認 URL 是 /login, 之前是 /j_spring_security_check 在 Spring Security 4。我們可以使用 loginProcessingUrl 方法來覆蓋此 URL:
http.formLogin()
.loginProcessingUrl("/perform_login")
我們可以使用 XML 配置:
login-processing-url="/perform_login"
通過覆蓋此默認 URL,我們正在隱藏應用程序實際上使用 Spring Security 的事實。此信息不應對外暴露。
8.3. The Landing Page on Success
成功登錄後,我們將重定向到一個頁面,默認情況下是 Web 應用程序的根目錄。我們可以通過 defaultSuccessUrl() 方法 覆蓋它:
http.formLogin()
.defaultSuccessUrl("/homepage.html")
或者使用 XML 配置:
default-target-url="/homepage.html"
如果 always-use-default-target 屬性設置為 true,則用户始終重定向到此頁面。如果該屬性設置為 false,則用户將重定向到他們之前想要訪問的上一頁。
8.4. The Landing Page on Failure
與登錄頁面類似,登錄失敗頁面由 Spring Security 自動生成在 /login?error 默認情況下。要覆蓋它,我們可以使用 failureUrl() 方法:
http.formLogin()
.failureUrl("/login.html?error=true")
或者使用 XML:
authentication-failure-url="/login.html?error=true"
結論
在本Spring 登錄示例中,我們配置了一個簡單的身份驗證流程。我們還討論了 Spring Security 登錄表單、安全配置以及一些高級自定義選項。
當項目在本地運行時,樣板 HTML 可在以下位置訪問:
http://localhost:8080/spring-security-mvc-login/login.html