Login For a Spring Web App – Error Handling and Localization – Spring Web应用程序的登录 – 错误处理和本地化

最后修改: 2014年 8月 24日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article we’re going to illustrate how to implement a simple Login Page with Spring MVC for an application that’s handling the authentication with Spring Security in the backend.

在这篇文章中,我们将说明如何用Spring MVC实现一个简单的登录页面,该应用在后端用Spring Security处理认证。

For the full details on how to handle login with Spring Security, here’s the article going in depth into the configuration and implementation of that.

关于如何使用 Spring Security 处理登录的全部细节,这里的文章深入介绍了该配置和实施。

2. The Login Page

2.登录页面

Let’s start by defining a very simple login page:

让我们从定义一个非常简单的登录页面开始

<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>

Now, let’s include a client side check to make sure the username and password have been entered before we even submit the form. For this example we’ll use plain Javascript, but JQuery is an excellent option as well:

现在,让我们包括一个客户端检查,以确保用户名密码在我们提交表单之前已经被输入。对于这个例子,我们将使用普通的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>

As you can see, we simply check if the username or password fields are empty; if they are – a javascript message box will pop up with the corresponding message.

正如你所看到的,我们只需检查用户名密码字段是否为空;如果是的话–将弹出一个带有相应信息的javascript消息框。

3. Message Localization

3.信息的本地化

Next – let’s localize the messages we’re using on the front end. There are types of such messages, and each is localized in a different manner:

接下来–让我们对我们在前端使用的消息进行本地化。这种信息有多种类型,每一种都以不同的方式进行本地化。

  1. Messages generated before the form is processed by Spring’s controllers or handlers. These messages ca be referenced in the JSP pages and are localized with Jsp/Jslt localization (see Section 4.3.)
  2. Messages that are localized once a page has been submitted for processing by Spring (after submitting the login form); these messages are localized using Spring MVC localization (See Section 4.2.)

3.1. The message.properties Files

3.1.message.properties文件

In either case, we need to create a message.properties file for each language we want to support; the names of the files should follow this convention: messages_[localeCode].properties.

无论是哪种情况,我们都需要为我们想要支持的每种语言创建一个message.properties文件;文件的名称应该遵循这个惯例。messages_[localeCode].properties

For example, if we want to support English and Spanish error messages we would have the file: messages_en.properties and messages_es_ES.properties. Note that, for English – messages.properties is also valid.

例如,如果我们想支持英语和西班牙语的错误信息,我们会有这样的文件。messages_en.propertiesmessages_es_ES.properties。请注意,对于英语–messages.properties也是有效的。

We’re going to place these two files in the project’s classpath (src/main/resources). The files simply contain the error codes and messages we need to display in different languages – for example:

我们将把这两个文件放在项目的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. Configuring Spring MVC Localization

3.2.配置Spring MVC的本地化

Spring MVC provides a LocaleResolver that works in conjunction with its LocaleChangeInterceptor API to make possible the display of messages in different languages, depending on the locale setting. To configure localization – we need to define the following beans in our MVC configuration:

Spring MVC提供了一个 LocaleResolver,它与它的 LocaleChangeInterceptor 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;
}

By default, the locale resolver will obtain the locale code from the HTTP header. To force a default locale, we need to set it on the localeResolver():

默认情况下,locale解析器将从HTTP头中获取locale代码。要强制使用默认的locale,我们需要在localeResolver()上设置它。

@Bean
public LocaleResolver localeResolver() {
    CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
    cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH);
    return cookieLocaleResolver;
}

This locale resolver is a CookieLocaleResolver which means that it stores the locale information in a client-side cookie; as such – it will remember the user’s locale every time they log in, and during the entire visit.

这个定位解析器是一个CookieLocaleResolver,这意味着它将定位信息存储在客户端的cookie中;因此–它将记住用户每次登录时的定位,以及整个访问期间的定位。

alternatively, there is a SessionLocaleResolver, which remembers the locale throughout the session. To use this LocaleResolver instead, we need to replace the above method with the following:

或者,有一个SessionLocaleResolver,它在整个会话中记住了locale。为了使用这个LocaleResolver,我们需要用下面的方法来代替上述方法。

@Bean
public LocaleResolver localeResolver() {
    SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
    return sessionLocaleResolver;
}

Lastly, note that the LocaleChangeInterceptor will change the locale based on the value of a lang parameter sent with the login page by simple links:

最后,请注意,LocaleChangeInterceptor将根据简单链接随登录页面发送的lang参数的值来改变locale。

<a href="?lang=en">English</a> |
<a href="?lang=es_ES">Spanish</a>

3.3. JSP/JSLT Localization

3.3.JSP/JSLT的本地化

JSP/JSLT API will be used to display localized messages that are caught in the jsp page itself. To use the jsp localization libraries we should add the following dependencies to the pom.xml:

JSP/JSLT 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. Displaying Error Messages

4.显示错误信息

4.1. Login Validation Errors

4.1.登录验证错误

In order to use the JSP/JSTL support and display localized messages in the login.jsp lets implement the following changes in the page:

为了使用JSP/JSTL支持并在login.jsp中显示本地化的信息,让我们在该页面中实现以下变化。

1. Add the following tag lib element to the login.jsp:

1.在 login.jsp中添加以下标签lib元素。

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

2. Add the jsp/jslt element that will point to the messages.properties files:

2.添加jsp/jslt元素,它将指向messages.properties文件。

<fmt:setBundle basename="messages" />

3. Add the following <fmt:…> elements to store the messages on jsp variables:

3.添加以下<fmt:…>元素来存储jsp变量上的信息。

<fmt:message key="message.password" var="noPass" />
<fmt:message key="message.username" var="noUser" />

4. Modify the login validation script we saw in Section 3 so as to localize the error messages:

4.修改我们在第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. Pre Login Errors

4.2.登录前的错误

Sometimes the login page will be passed an error parameter if the previous operation failed. For example, a registration form submit button will load the login page. If the registration was successful, then it would be a good idea to show a success message in the login form, and an error message if the opposite was true.

有时,如果之前的操作失败了,登录页面会被传递一个错误参数。例如,一个注册表单的提交按钮将加载登录页面。如果注册成功,那么在登录表单中显示一个成功信息是个好主意,如果相反,则显示一个错误信息。

In the sample login form below, we are implementing this by intercepting and regSucc and regError parameters, and displaying a localized message based on their values.

在下面的示例login表单中,我们通过拦截和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. Login Security Errors

4.3.登录安全错误

In case the login process fails for some reason, Spring Security will do a redirect to a login error URL, which we have defined to be /login.html?error=true.

如果登录过程由于某种原因而失败,Spring Security会重定向到一个登录错误的URL,我们将其定义为/login.html?error=true

So – similarly to how we have shown the status of the registration in the page, we need to do the same in case of a login problem:

因此–与我们在页面中显示注册状态的方式类似,我们需要在出现登录问题时做同样的事情。

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

Notice that we are using a <spring:message …> element. This means that the error messages are generated during the Spring MVC processing.

注意我们使用的是 <spring:message …>/em>元素。这意味着错误信息是在Spring MVC处理过程中产生的。

The full login page – including the js validation and these additional status messages can be found in the github project.

完整的登录页面–包括js验证和这些额外的状态信息,可以在github项目中找到。

4.4. Logout Errors

4.4.注销错误

In the example that follows, the jsp code <c:if test=”${not empty SPRING_SECURITY_LAST_EXCEPTION}”> in the logout.html page will check if there was an error in the logout process.

在下面的例子中,jsp代码<c:if test=”${not empty SPRING_SECURITY_LAST_EXCEPTION}”> logout.html页面中,将检查在注销过程中是否有错误。

For example – if there is a persistence exception when a custom logout handler tries to store user data before redirecting to the logout page. While these errors are rare, we should handle them as neatly as possible as well.

例如–当一个自定义注销处理程序试图在重定向到注销页面之前存储用户数据时,出现了持久性异常。虽然这些错误很少,但我们也应该尽可能整齐地处理它们。

Lets take a look at the complete logout.jsp:

让我们看一下完整的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>

Notice that the logout page also reads the query string param logSucc, and if its value equal to true, a localized success message will be displayed.

注意,注销页面也会读取查询字符串参数 logSucc,如果其值等于true,将显示一个本地化的成功信息。

5. The Spring Security Configuration

5.Spring的安全配置

The focus of this article is the frontend of the login process, not the backend – so we’ll look only briefly at the main points of the security configuration; for the full config, you should read the previous article.

本文的重点是登录过程的前端,而不是后端–因此我们只简要地看一下安全配置的要点;要想了解完整的配置,你应该阅读前文

5.1. Redirecting to the Login Error URL

5.1.重定向到登录错误的URL

The following directive in the <form-login…/> element directs the flow of the application to the url where the login error will be handled:

<form-login…/>元素中的以下指令将应用程序的流程引导到将处理登录错误的url。

authentication-failure-url="/login.html?error=true"

5.2. The Logout Success Redirect

5.2.注销成功的重定向

<logout 
  invalidate-session="false" 
  logout-success-url="/logout.html?logSucc=true" 
  delete-cookies="JSESSIONID" />

The logout-success-url attribute simply redirects to the logout page with a parameter that confirms that the logout was successful.

logout-success-url属性只是重定向到注销页面,并有一个参数确认注销是否成功。

6. Conclusion

6.结论

In this article we have illustrated how to implement a Login page for a Spring Security backed application – handling login validation, displaying authentication errors and message localization.

在这篇文章中,我们说明了如何为Spring Security支持的应用程序实现一个登录页面–处理登录验证、显示认证错误和消息本地化。

We’re going to be looking at a full registration implementation in the next article – with the goal of having a full implementation of the login and registration process ready for production.

我们将在下一篇文章中研究一个完整的注册实现–目标是为生产准备一个完整的登录和注册过程的实现。