1. 概述
本文將演示如何使用 Spring MVC 實現一個簡單的登錄頁面,用於處理 Spring Security 在後端進行的身份驗證。
對於如何使用 Spring Security 進行身份驗證的詳細信息,本文將深入探討配置和實現過程。
2. 登錄頁面
讓我們從定義一個非常簡單的登錄頁面開始:
<html>
<head></head>
<body>
<h1>Login</h1>
<form name='f' action="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>
</body>
</html>現在,讓我們添加一個客户端檢查,以確保在提交表單之前,用户名和密碼已經輸入。對於這個例子,我們將使用純 JavaScript,但 jQuery 也是一個不錯的選擇:
<script type="text/javascript">
function validate() {
if (document.f.username.value == "" && document.f.password.value == "") {
alert("Username and password are required");
document.f.username.focus();
return false;
}
if (document.f.username.value == "") {
alert("Username is required");
document.f.username.focus();
return false;
}
if (document.f.password.value == "") {
alert("Password is required");
document.f.password.focus();
return false;
}
}
</script>如您所見,我們只需檢查 username 或 password 字段是否為空;如果為空 – 一個 JavaScript 消息框會彈出,顯示相應的提示信息。
3. 消息本地化
接下來,讓我們對前端使用的消息進行本地化。 這種消息有多種類型,每種類型採用不同的本地化方式:
- 生成於表單在 Spring 控制器或處理器處理之前的消息。 這些消息可以引用 JSP 頁面,並使用 Jsp/Jslt 本地化 (參見第 4.3 節) 進行本地化。
- 消息是在頁面提交併由 Spring 處理(例如提交 login 表單)後本地化的;這些消息使用 Spring MVC 本地化 (參見第 4.2 節) 進行本地化。
3.1. 消息文件 (message.properties) 文件
無論採用哪種方式,我們都需要為每個支持的語言創建一個
例如,如果我們想支持英文和西班牙語的錯誤消息,我們會擁有以下文件:
我們將這些文件放置在項目的類路徑中 (
message.username=Username required
message.password=Password required
message.unauth=Unauthorized access!!
message.badCredentials=Invalid username or password
message.sessionExpired=Session timed out
message.logoutError=Sorry, error login out
message.logoutSucc=You logged out successfully3.2. 配置 Spring MVC 國際化
Spring MVC 提供了一個 <em LocaleResolver</em> 接口,與它的 <em LocaleChangeInterceptor</em> API 協同工作,從而實現根據當前區域設置顯示不同語言的消息。為了配置國際化,我們需要在 MVC 配置中定義以下 Bean:
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
return cookieLocaleResolver;
}
默認情況下,本地化解析器將從 HTTP 標頭中獲取本地化代碼。要強制使用默認本地化,需要將其設置為 <em localeResolver():
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH);
return cookieLocaleResolver;
}此區域解析器是一個 CookieLocaleResolver,這意味着它將區域信息存儲在客户端 Cookie 中;因此,它會記住用户在每次登錄和整個訪問期間的區域信息。
或者,還有 SessionLocaleResolver,它會記住會話期間的區域信息。要使用此 LocaleResolver 代替上述方法,我們需要用以下方法替換它:
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
return sessionLocaleResolver;
}最後,請注意,LocaleChangeInterceptor 將根據登錄頁面通過簡單鏈接發送的 lang 參數的值來更改語言環境:
<a href="?lang=en">English</a> |
<a href="?lang=es_ES">Spanish</a>
<h3><strong>3.3. JSP/JSLT 國際化</strong></h3>
<p>JSP/JSLT API 將用於在 JSP 頁面本身捕獲的本地化消息進行顯示。為了使用 JSP 國際化庫,我們應該將以下依賴項添加到 <em >pom.xml</em> 中:</p>
<dependency>
<groupId>jakarta.servlet.jsp</groupId>
<artifactId>jakarta.servlet.jsp-api</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>4. 顯示錯誤消息
This section describes how to display error messages to the user. Error messages provide valuable feedback to the user about what went wrong and how to fix it. It’s crucial to present error messages in a clear, concise, and user-friendly manner.
Here are some best practices for displaying error messages:
- Be Specific: Don't just say "An error occurred." Provide details about the error, such as the specific field that caused the error or the reason for the failure.
- Use Clear Language: Avoid technical jargon that the user may not understand. Use plain language to explain the error.
- Provide Guidance: Tell the user what they can do to fix the error. For example, "Please enter a valid email address" or "The file you are trying to upload is too large."
- Use Appropriate Formatting: Use color, icons, or other visual cues to highlight error messages.
- Log Errors for Debugging: In addition to displaying error messages to the user, log the errors to a file or database for debugging purposes.
Example:
// This code demonstrates how to display an error message to the user.
// If the user enters an invalid email address, this function will display an error message.
function displayErrorMessage(message) {
// Display the error message to the user.
alert("Error: " + message);
}
// Example usage:
displayErrorMessage("Invalid email address. Please enter a valid email address.");
4.1. 登錄驗證錯誤
為了使用 JSP/JSTL 支持並顯示 login.jsp 中的本地化消息,請在頁面中實施以下更改:
- 將以下標籤庫元素添加到 login.jsp 中:
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>2. 添加指向 messages.properties 文件的 jsp/jslt 元素:
<fmt:setBundle basename="messages" />- 將以下 <em> <fmt:…></em> 元素添加到 JSP 變量中以存儲消息:
<fmt:message key="message.password" var="noPass" />
<fmt:message key="message.username" var="noUser" />- 修改我們在第 3 節中看到的登錄驗證腳本,以便本地化錯誤消息:
<script type="text/javascript">
function validate() {
if (document.f.username.value == "" && document.f.password.value == "") {
alert("${noUser} and ${noPass}");
document.f.username.focus();
return false;
}
if (document.f.username.value == "") {
alert("${noUser}");
document.f.username.focus();
return false;
}
if (document.f.password.value == "") {
alert("${noPass}");
document.f.password.focus();
return false;
}
}
</script>4.2. 預登錄錯誤
有時登錄頁面會接收到錯誤參數,如果之前的操作失敗。例如,註冊表單提交按鈕會加載登錄頁面。如果註冊成功,那麼在登錄表單中顯示成功消息是一個好主意,如果相反,則顯示錯誤消息也是如此。
在下面的示例 登錄表單中,我們通過攔截 regSucc 和 regError 參數並根據其值顯示本地化消息來實現此目的。
<c:if test="${param.regSucc == true}">
<div id="status">
<spring:message code="message.regSucc">
</spring:message>
</div>
</c:if>
<c:if test="${param.regError == true}">
<div id="error">
<spring:message code="message.regError">
</spring:message>
</div>
</c:if>4.3. 登錄安全錯誤
如果由於某種原因登錄過程失敗,Spring Security 將會重定向到定義的登錄錯誤 URL,該 URL 為 /login.html?error=true。
因此,類似於我們在註冊頁面上顯示註冊狀態,在登錄問題發生時,我們也需要執行相同的操作:
<c:if test="${param.error != null}">
<div id="error">
<spring:message code="message.badCredentials">
</spring:message>
</div>
</c:if>請注意,我們正在使用一個 <spring:message …> 元素。這意味着錯誤消息是在 Spring MVC 處理過程中生成的。
完整的登錄頁面——包括 js 驗證和這些附加狀態消息,可以在 github 項目 中找到。
4.4. 註銷錯誤
在下面的示例中, JSP 代碼 <c:if test=”${not empty SPRING_SECURITY_LAST_EXCEPTION}”> 在 logout.html 頁面中會檢查註銷過程中是否存在錯誤。
例如 – 如果在自定義註銷處理程序嘗試在重定向到註銷頁之前存儲用户數據時,出現持久性異常,則會發生這種情況。 雖然這些錯誤很少見,但我們應該儘可能地處理它們。
以下是完整的 logout.jsp 頁面:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec"
uri="http://www.springframework.org/security/tags"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}">
<div id="error">
<spring:message code="message.logoutError">
</spring:message>
</div>
</c:if>
<c:if test="${param.logSucc == true}">
<div id="success">
<spring:message code="message.logoutSucc">
</spring:message>
</div>
</c:if>
<html>
<head>
<title>Logged Out</title>
</head>
<body>
<a href="login.html">Login</a>
</body>
</html>請注意,登出頁面還會讀取查詢字符串參數 logSucc,如果其值等於 true,則會顯示本地化的成功消息。
5. Spring Security 配置
本文重點介紹登錄流程的前端,而非後端——因此,我們將僅對 Spring Security 配置的主要要點進行簡要概述;完整的配置請參考上一篇文章。
5.1. 將流量重定向到登錄錯誤 URL
以下在<em >form-login…/> 元素中的指令,將應用程序的流程重定向到處理登錄錯誤的 URL:
authentication-failure-url="/login.html?error=true"5.2. 登出成功重定向
<logout
invalidate-session="false"
logout-success-url="/logout.html?logSucc=true"
delete-cookies="JSESSIONID" />logout-success-url 屬性只需將用户重定向到登出頁面,並傳遞一個參數以確認登出操作已成功。
6. 結論
在本文中,我們演示瞭如何為 Spring Security 支持的應用程序實現登錄頁面——包括處理登錄驗證、顯示身份驗證錯誤和消息本地化。
在下一篇文章中,我們將探討完整的註冊實施——目標是為生產環境準備好登錄和註冊過程的完整實現。