[譯] 學習 Spring Security(二):錯誤處理與本地化

Oopsguy發表於2017-11-03

www.baeldung.com/spring-secu…
作者:Eugen Paraschiv
譯者:oopsguy.com
公眾號:oopsguy_com

1、概述

在本文中,我們將介紹如何使用 Spring MVC 實現一個簡單的登入頁面,該應用程式在後端使用 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>複製程式碼

您可以看到,我們只需檢查使用者名稱或密碼欄位是否為空,如果為空,將彈出一個 javascript 彈窗以顯示相應的訊息。

3、Message 本地化

接下來,讓我們將正在使用的訊息本地化應用到前端。有以下訊息型別,每個都以不同的方式進行本地化:

  1. 在 Spring 的控制器或 handler 處理表單之前生成的訊息。這些訊息可以在 JSP 頁面中引用,並使用 Jsp/Jstl 進行本地化(參見第 4.3 節)
  2. 一旦頁面已提交給 Spring 進行處理(在提交登入表單之後)本地化的訊息,這些訊息將使用 Spring MVC 進行本地化(參見第 4.2 節)

3.1、message.properties 檔案

在這兩種情況下,我們需要為要支援的每種語言建立一個 message.properties 檔案。檔案的名稱應遵循以下約定:messages_[localeCode].properties

例如,如果我們要支援英語和西班牙語錯誤訊息,我們需要以下檔案:messages_en.propertiesmessages_es_ES.properties。請注意,對於英文,命名為 messages.properties 也是可以。

我們將把這兩個檔案放在專案的 classpath中(src/main/resources)。這些檔案只包含我們需要用到的不同語言的錯誤程式碼和訊息,例如:

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 successfully複製程式碼

3.2、配置 Spring MVC 本地化

Spring MVC 提供了一個 LocaleResolver,它與 LocaleChangeInterceptor API 一起使用,可根據區域(locale)設定顯示不同語言的訊息。要配置本地化,我們需要在 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;
}
@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = 
      new ReloadableResourceBundleMessageSource();
    messageSource.setBasename("classpath:messages");
    messageSource.setUseCodeAsDefaultMessage(true);
    messageSource.setDefaultEncoding("UTF-8");
    messageSource.setCacheSeconds(0);
    return messageSource;
}複製程式碼

這裡有一個重要和更高階的注意事項是,我們需要確保 messageSource bean 定義在正確的 Spring 上下文中 — 它將在此上下文中使用。請記住,Web 應用程式具有根上下文,並且有一個(或多個)servlet 上下文。

預設情況下,locale 解析器(resolver)將從 HTTP 頭獲取區域程式碼。要強制使用預設語言環境,我們需要在 localeResolver() 上設定它:

@Bean
public LocaleResolver localeResolver() {
    CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
    cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH);
    return cookieLocaleResolver;
}複製程式碼

該 locale 解析器是一個 CookieLocaleResolver,這意味著它將客戶端 cookie 中的 locale 資訊儲存起來。 因此,它將在每次登入時以及整個訪問過程中記住使用者的 locale 設定。

此外,還有一個 SessionLocaleResolver,可以記住整個會話中的 locale 設定。 要使用此 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>複製程式碼

3.3、JSP/JSTL 本地化

JSP/JSTL API 將用於顯示在 jsp 頁面獲取的本地化訊息。要使用 jsp 本地化庫,我們應該將以下依賴項新增到 pom.xml 中:

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.2-b01</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>複製程式碼

4、顯示錯誤資訊

4.1、登入驗證錯誤

為了使用 JSP/JSTL 支援並在 login.jsp 中顯示本地化訊息,您可以在頁面中實現以下更改:

  1. 將以下 taglib 標籤新增到 login.jsp 中:
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>複製程式碼
  1. 新增指向 messages.properties 檔案的 jsp/jslt 標籤:
<fmt:setBundle basename="messages" />複製程式碼
  1. 新增以下 標籤以將訊息儲存在 jsp 變數中:
<fmt:message key="message.password" var="noPass" />
<fmt:message key="message.username" var="noUser" />複製程式碼
  1. 修改我們在第 2 節中的登入驗證指令碼,以便本地化錯誤訊息:
<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、登入前錯誤

如果之前的操作發生失敗,將被傳遞一個錯誤引數到登入頁面。例如,登錄檔單提交按鈕將載入登入頁面。 如果註冊成功,則在登入表單中顯示成功訊息,如果失敗,則會出現錯誤訊息。

在下面的登入表單示例中,我們通過攔截與 regSuccregError 引數來實現,並根據它們的值顯示本地化訊息。

<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,我們將其指定為 /login.html?error=true

因此,類似於如何在頁面中顯示註冊狀態,我們需要在登入發生問題的情況下做同樣的處理:

<c:if test="${param.error != null}">
    <div id="error">
        <spring:message code="message.badCredentials">   
        </spring:message>
    </div>
</c:if>複製程式碼

請注意,我們使用了 標籤。這意味著在 Spring MVC 處理期間將生成錯誤訊息。

完整的登入頁面包括了 js 驗證和附加的狀態訊息,您可以在 github 專案中找到。

4.4、登出錯誤

在下面的示例中,logout.html 頁面中的 jsp 程式碼 將檢查登出過程中是否有存在錯誤。

例如,如果自定義登出 handler 在重定向到登出頁面之前嘗試儲存使用者資料時發生了持久化異常。 雖然這些錯誤很罕見,但我們也應該儘可能地處理。

讓我們來看看完整的 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 配置

本文的重點是前端的登入過程,而不是後端。

5.1、重定向到登入錯誤 URL

指令將應用程式的引導到處理登入錯誤 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 支援的應用程式實現 Login 頁面以及處理登入驗證、顯示身份驗證錯誤和訊息本地化。

相關程式碼和連結

相關文章