1. 概述
用户在已登錄狀態下,經常無法訪問登錄頁面。 常見的做法是將用户重定向到另一個頁面,通常是在登錄後應用程序的起始點。
在本教程中,我們將探討使用 Spring Security 實現此解決方案的多種方法。
此外,為了更快速地實現登錄功能,我們可以參考 這篇文章。
2. 身份驗證驗證
首先,我們需要一種驗證身份驗證的方法。
換句話説,我們需要從 SecurityContext 中獲取身份驗證詳情,並驗證用户是否已登錄:
private boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || AnonymousAuthenticationToken.class.
isAssignableFrom(authentication.getClass())) {
return false;
}
return authentication.isAuthenticated();
}
我們將在此後所有負責重定向的組件中使用它。
3. 從登錄控制器重定向
實現我們的目標最簡單的方法是,在控制器中定義登錄頁面的端點。
我們還需要在用户已認證時返回特定的頁面,否則返回登錄頁面:
@GetMapping("/loginUser")
public String getUserLoginPage() {
if (isAuthenticated()) {
return "redirect:userMainPage";
}
return "loginUser";
}
4. 使用攔截器
通過 URI 上的攔截器,另一種重定向用户的方式是登錄頁面的攔截器。
攔截器將在請求到達控制器之前攔截請求。因此,我們可以根據身份驗證情況決定是否允許其繼續,或者阻止其並返回重定向響應。
如果用户已認證,我們需要修改響應中的兩項內容:
- 設置狀態碼為 HttpStatus.SC_TEMPORARY_REDIRECT
- 添加 Location 標頭,包含重定向 URL
最後,我們將通過返回 false 終止執行鏈:
public class LoginPageInterceptor implements HandlerInterceptor {
UrlPathHelper urlPathHelper = new UrlPathHelper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if ("/loginUser".equals(urlPathHelper.getLookupPathForRequest(request)) && isAuthenticated()) {
String encodedRedirectURL = response.encodeRedirectURL(
request.getContextPath() + "/userMainPage");
response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
response.setHeader("Location", encodedRedirectURL);
return false;
} else {
return true;
}
}
// isAuthenticated method
}
我們也需要將攔截器添加到 Spring MVC 生命週期
@Configuration
public class LoginRedirectMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginPageInterceptor());
}
}
我們也可以使用 Spring 的基於 XML 模式來實現相同的功能:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/loginUser"/>
<bean class="com.baeldung.loginredirect.LoginPageInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
5. 使用過濾器
類似於,我們可以實現一個 Spring 過濾器。
該過濾器可以直接應用於 SecurityContext,利用 Spring Security 的過濾器鏈。因此,它可以在身份驗證創建後立即攔截請求。
public class LoginPageFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse) response;
if (isAuthenticated() && "/loginUser".equals(servletRequest.getRequestURI())) {
String encodedRedirectURL = ((HttpServletResponse) response).encodeRedirectURL(
servletRequest.getContextPath() + "/userMainPage");
servletResponse.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
servletResponse.setHeader("Location", encodedRedirectURL);
}
chain.doFilter(servletRequest, servletResponse);
}
// isAuthenticated 方法
}
我們需要在過濾器鏈中將過濾器添加到 UsernamePasswordAuthenticationFilter 之後。
此外,我們需要為登錄頁 URI 啓用過濾器鏈,以授權該請求:
@Configuration
@EnableWebSecurity
public class LoginRedirectSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.addFilterAfter(new LoginPageFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry.requestMatchers("/loginUser").permitAll()
// Other security configuration
}
}
最後,如果選擇使用 XML 配置,則可以定義過濾器的 Bean,並在安全 HTTP 標籤中將其添加到過濾器鏈中:
<beans:bean id="loginPageFilter" class="com.baeldung.loginredirect.LoginPageFilter"/>
<security:http pattern="/**" use-expressions="true" auto-config="true">
<security:intercept-url pattern="/loginUser" access="permitAll"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="loginPageFilter"/>
</security:http>
有關如何為 Spring Security 創建自定義過濾器的快速教程,請參見此處。
6. 結論
在本教程中,我們探討了如何使用 Spring Security 將已登錄的用户從登錄頁面重定向的多種方法。
另一個可能對您有幫助的教程是“使用 Spring Security 在登錄後重定向到不同頁面”,其中我們學習瞭如何將不同類型的用户重定向到特定頁面。