转载

学习 Spring Security(二)错误处理与本地化

在本文中,我们将介绍如何使用 Spring MVC 实现一个 简单的登录页面 ,该应用程序在后端使用 Spring Security 进行身份验证。

2、登录页面

我们首先定义一个非常简单的登录页面:

<html>
<head></head>
<body>
   <h1>Login</h1>
   <formname='f'action="login"method='POST'>
      <table>
         <tr>
            <td>User:</td>
            <td><inputtype='text'name='username'value=''></td>
         </tr>
         <tr>
            <td>Password:</td>
            <td><inputtype='password'name='password'/></td>
         </tr>
         <tr>
            <td><inputname="submit"type="submit"value="submit"/></td>
         </tr>
      </table>
  </form>
</body>
</html>

现在,让我们添加客户端检查,以确保在提交表单之前用户名和密码不为空。针对这个例子,我们将使用简单的 Javascript,也许 JQuery 是一个更好的选择:

<scripttype="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 参数的值来更改语言环境:

<ahref="?lang=en">English</a> |
<ahref="?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 节中的登录验证脚本,以便本地化错误消息:
<scripttype="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属性简单地重定向到注销页面,该参数确定注销成功。

原文  http://oopsguy.com/2017/11/04/spring-security-login-error-handling-localization/
正文到此结束
Loading...