技术概述

 

 

技术概述

5.1. 运行环境

Spring Security 3.0需要运行在Java 5.0或更高版本环境上。 因为Spring Security的目标是自己容器内管理, 所以不需要为你的Java运行环境进行什么特别的配置。 特别是,不需要特别配置一个Java Authentication and Authorization Service (JAAS)政策文件, 也不需要把Spring Security放到server的classLoader下。

相同的,如果你使用了一个EJB容器或者是Servlet容器,都不需要把任何 特定的配置文件放到什么地方,也不需要把Spring Security放到server的classloader下。 所有必须的文件都可以配置在你的应用中。

这些设计确保了发布时的最大轻便性, 你可以简单把你的目标文件(JAR或WAR或EAR)从一个系统复制到另一个系统, 它会立即正常工作。

5.2. 核心组件

在Spring Security 3.0中,spring-security-corejar的内容 已经被缩减到最小。它不再包含任何与web应用安全, LDAP或命名空间相关的代码。我们会看一下这里, 看看你在核心模块中找到的Java类型。它们展示了框架的构建基础, 所以如果你需要超越简单的命名空间配置, 那么理解它们就是很重要的,即便你不需要直接 操作他们。

5.2.1. SecurityContextHolder, SecurityContext 和 Authentication对象

最基础的对象就是SecurityContextHolder。 我们把当前应用程序的当前安全环境的细节存储到它里边了, 它也包含了应用当前使用的主体细节。 默认情况下,SecurityContextHolder使用ThreadLocal存储这些信息, 这意味着,安全环境在同一个线程执行的方法一直是有效的, 即使这个安全环境没有作为一个方法参数传递到那些方法里。 这种情况下使用ThreadLocal是非常安全的, 只要记得在处理完当前主体的请求以后,把这个线程清除就行了。 当然,Spring Security自动帮你管理这一切了, 你就不用担心什么了。

有些程序并不适合使用ThreadLocal, 因为它们处理线程的特殊方法。比如,swing客户端也许希望 JVM里的所有线程都使用同一个安全环境。 SecurityContextHolder可以使用一个策略进行配置 在启动时,指定你想让上下文怎样被保存。对于一个单独的应用系统,你可以使用 SecurityContextHolder.MODE_GLOBAL策略。 其他程序可能想让一个线程创建的线程也使用相同的安全主体。 这时可以使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL。 想要修改默认的SecurityContextHolder.MODE_THREADLOCAL模式,可以使用两种方法。 第一个是设置系统属性。另一个是调用 SecurityContextHolder的静态方法。大多数程序不需要修改默认值, 但是如果你需要做修改,先看一下 SecurityContextHolder的JavaDoc中的详细信息。

5.2.1.1. 获得当前用户的信息

我们把安全主体和系统交互的信息都保存在 SecurityContextHolder中了。 Spring Security使用一个Authentication对应来表现这些信息。 虽然你通常不需要自己创建一个Authentication对象, 但是常见的情况是,用户查询 Authentication对象。你可以使用下面的代码 - 在你程序中的任何位置 - 来获得已认证用户的名字, 比如:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
  String username = ((UserDetails)principal).getUsername();
} else {
  String username = principal.toString();
}

调用getContext()返回的对象是一个 SecurityContext接口的实例。 这个对象是保存在thread-local中的。如我们下面看到的,大多数Spring Security的验证机制 都返回一个UserDetails的实例 作为主体。

5.2.2. UserDetailsService

从上面的代码片段中还可以看出另一件事,就是你可以从Authentication对象中获得安全主体。 这个安全主体就是一个对象。 大多数情况下,可以强制转换成UserDetails对象。 UserDetails是一个Spring Security的核心接口。 它代表一个主体,是扩展的,而且是为特定程序服务的。 想一下UserDetails章节,在你自己的用户数据库和如何把Spring Security需要的数据放到SecurityContextHolder里。 为了让你自己的用户数据库起作用,我们常常把UserDetails转换成你系统提供的类,这样你就可以直接调用业务相关的方法了(比如getEmail()getEmployeeNumber()等等)。

现在,你可能想知道,我应该什么时候提供这个UserDetails对象呢? 我怎么做呢? 我想你说这个东西是声明式的,我不需要写任何代码,怎么办? 简单的回答是,这里有一个特殊的接口,叫UserDetailsService。 这个接口里的唯一一个方法,接收String类型的用户名参数,返回UserDetails

  UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

这是获得从Spring Security中获得用户信息的最常用方法,你会看到它在框架中一直被用到。 当需要获得一个用户的信息的时候。

当成功通过验证时,UserDetails会被用来 建立Authentication对象,保存在SecurityContextHolder里。 (更多的信息可以参考下面的#tech-intro-authentication-mgr)。 好消息是我们提供了好几个UserDetailsService实现,其中一个使用了 内存中的map(InMemoryDaoImpl)另一个而是用了JDBC (JdbcDaoImpl)。 虽然,大多数用户倾向于写自己的,使用这些实现常常放到已有的数据访问对象(DAO)上,表示它们的雇员,客户或其他企业应用中的用户。 记住这个优势,无论你用什么UserDetailsService返回的数据都可以通过SecurityContextHolder获得,就像上面的代码片段讲的一样。

Note

通常对UserDetailsService会有这样的迷惑。 它完全是一个数据信息的DAO,没有提供的功能在框架内提供其他组件的信息。 特别是,它不会 认证用户,那是AuthenticationManager做的。 大多数情况下,直接实现 AuthenticationProvider,如果你需要一个自定义的认证流程。

5.2.3. GrantedAuthority

除了主体,另一个Authentication提供的重要方法是getAuthorities()。 这个方法提供了GrantedAuthority对象数组。 毫无疑问,GrantedAuthority是赋予到主体的权限。 这些权限通常使用角色表示,比如ROLE_ADMINISTRATOR 或 ROLE_HR_SUPERVISOR。 这些角色会在后面,对web验证,方法验证和领域对象验证进行配置。 Spring Security的其他部分用来拦截这些权限,期望他们被表现出现。 GrantedAuthority对象通常使用UserDetailsService读取的。

通常情况下,GrantedAuthority对象是应用程序范围下的授权。 它们不会特意分配给一个特定的领域对象。 因此,你不能设置一个GrantedAuthority,让它有权限展示编号54的Employee对象,因为如果有成千上网的这种授权,你会很快用光内存(或者,至少,导致程序花费大量时间去验证一个用户)。 当然,Spring Security被明确设计成处理常见的需求,但是你最好别因为这个目的使用项目领域模型安全功能。

5.2.4. 小结

简单回顾一下,Spring Security主要是由一下几部分组成的:

  • SecurityContextHolder,提供几种访问SecurityContext的方式。

  • SecurityContext,保存Authentication信息,和请求对应的安全信息。

  • HttpSessionContextIntegrationFilter,为了在不同请求使用,把SecurityContext保存到 HttpSession里。

  • Authentication,展示Spring Security特定的主体。

  • GrantedAuthority,反应,在应用程序范围你,赋予主体的权限。

  • UserDetails,通过你的应用DAO,提供必要的信息,构建Authentication对象。

  • UserDetailsService,创建一个 UserDetails,传递一个 String类型的用户名(或者证书ID或其他)。

现在,你应该对这种重复使用的组件有一些了解了。 让我们贴近看一下验证的过程。

5.3. 验证

Spring Security可以用在多种不同的验证环境下。 我们推荐人们使用Spring Security进行验证,而不是与现存的容器管理验证相结合, 然而这种方式也是被支持的 - 作为与你自己的 验证系统相整合的一种方式。

5.3.1. 什么是Spring Security的验证呢?

让我们考虑一种标准的验证场景,每个人都很熟悉的那种。

  1. 一个用户想使用一个账号和密码进行登陆。

  2. 系统(成功的)验证了密码对于这个用户名 是正确的。

  3. 这个用户对应的信息呗获取 (他们的角色列表以及等等)。

  4. 为用户建立一个安全环境。

  5. 用户会执行一些操作,这些都是潜在被 权限控制机制所保护的,通过对操作的授权, 使用当前的安全环境信息。

前三个项目执行了验证过程,所以我们可以看一下 Spring Security的作用。

  1. 用户名和密码被获得,并进行比对, 在一个UsernamePasswordAuthenticationToken的实例中 (它是Authentication接口的一个实例, 我们在之前已经见过了)。

  2. 这个标志被发送给一个AuthenticationManager 的实例进行校验。

  3. AuthenticationManager返回一个完全的 Authentication实例, 在成功校验后。

  4. 安全环境被建立,通过调用 SecurityContextHolder.getContext().setAuthentication(...), 传递到返回的验证对象中。

从这一点开始,用户已经通过校验了。让我们 看一些代码作为例子。

import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;

public class AuthenticationExample {
  private static AuthenticationManager am = new SampleAuthenticationManager();

  public static void main(String[] args) throws Exception {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    while(true) {
      System.out.println("Please enter your username:");
      String name = in.readLine();
      System.out.println("Please enter your password:");
      String password = in.readLine();
      try {
        Authentication request = new UsernamePasswordAuthenticationToken(name, password);
        Authentication result = am.authenticate(request);
        SecurityContextHolder.getContext().setAuthentication(result);
        break;
      } catch(AuthenticationException e) {
        System.out.println("Authentication failed: " + e.getMessage());
      }
    }
    System.out.println("Successfully authenticated. Security context contains: " +
              SecurityContextHolder.getContext().getAuthentication());
  }
}

class SampleAuthenticationManager implements AuthenticationManager {
  static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();

  static {
    AUTHORITIES.add(new GrantedAuthorityImpl("ROLE_USER"));
  }

  public Authentication authenticate(Authentication auth) throws AuthenticationException {
    if (auth.getName().equals(auth.getCredentials())) {
      return new UsernamePasswordAuthenticationToken(auth.getName(),
        auth.getCredentials(), AUTHORITIES);
      }
      throw new BadCredentialsException("Bad Credentials");
  }
}

这里 我们写了一些程序,询问用户输入一个用户名和密码, 然后执行上面的顺序。我们实现的AuthenticationManager 会验证所有用户名和密码一样的用户。 它为每个永固分配一个单独的角色。上面输出的信息 将会像这样:

Please enter your username:
bob
Please enter your password:
password
Authentication failed: Bad Credentials
Please enter your username:
bob
Please enter your password:
bob
Successfully authenticated. Security context contains: \
 org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: \
 Principal: bob; Password: [PROTECTED]; \
 Authenticated: true; Details: null; \
 Granted Authorities: ROLE_USER
        

注意,你没必要写这些代码。这些处理都是发生在内部的, 比如在一个web验证过滤器中。我们只是使用了这些代码, 来演示真实情况下的问题,Spring Security提供了一个简单的答案。 一个用户被验证,当 SecurityContextHolder包含了完整的 Authentiation对象。

5.3.2. 直接设置SecurityContextHolder的内容

实际上,Spring Security不知道你怎么把 Authentication对象放到 SecurityContextHolder里。唯一关键的要求是 SecurityContextHolder包含了一个 Authentication表示了一个主体, 在AbstractSecurityInterceptor之前(我们以后会看到更多) 需要验证一个用户操作。

你可以(许多人都这样做)写自己的过滤器,或MVC控制器来提供验证系统, 不基于Spring Security。比如 你可能使用容器管理的验证,让当前用户有效 在TheadLocal或JNDI位置。或者你可能为一个公司工作,你没有什么控制力。 这种情况下,使用Spring Security很简单, 还是提供验证功能。 你需要做的是些一个过滤器(或什么设备) 从一个地方读取第三方用户信息, 构建一个Spring Security特定的Authentication对象, 把它放到SecurityContextHolder里。 在这种情况下,你也需要考虑 那些平常由内置的架构自动提供的功能。 比如,你也许需要预先创建一个HTTP会话来 为不同的请求缓存上下文, 在你把响应内容写入到客户端之前[9]

如果你想知道AuthenticationManager是如何实现的, 我们会在***看到。

5.4. 在web应用中验证

现在让我们研究一下情景,当我们在web应用中使用Spring Security (不使用web.xml安全)。一个用户 如何验证,安全环境如何创建?

考虑一个典型的web应用的验证过程:

  1. 你访问主页,点击链接。

  2. 一个请求发送给服务器,服务器决定你是否在 请求一个被保护的资源。

  3. 如果你还没有授权,服务器发回一个相应,提示你必须登录。 响应会是一个HTTP响应代码, 或重定向到特定的web页面。

  4. 基于验证机制,你的浏览器会重定向到特殊的web页面, 所以你可以填写表单,或者浏览器会验证你的身份 (通过一个BASIC验证对话框,一个cookie,一个X.509 验证,等等)。

  5. 浏览器会发送回一个响应到服务器。这会是一个HTTP POST 包含你填写的表单中的内容,或者一个HTTP头部 包含你的验证细节。

  6. 下一步,服务器决定,当前证书是否有效。 如果它们有效,下一步会执行。如果它们无效,通常你的浏览器会 被询问再试一次(所以initial返回上两步)。

  7. 你的原始请求会引发验证过程。 希望你验证了获得了授予的权限来访问被保护的资源。 如果你完允许访问,请求会成功。 否则,你会收到一个HTTP错误代码403,意思是“拒绝访问”。

Spring Security拥有不同的泪,对应很多常用的上面所说的步骤。 主要的部分(使用的次序)是ExceptionTranslationFilter, 一个AuthenticationEntryPoint和一个 验证机制”, 对应着AuthenticationManager的调用 我们在上一章见过。

5.4.1. ExceptionTranslationFilter

ExceptionTranslationFilter是一个Spring Security过滤器 负责检测任何一个Spring Security抛出的异常。 这些异常会被AbstractSecurityInterceptor抛出, 这是一个验证服务的主要提供器。我们会在下一章讨论AbstractSecurityInterceptor, 而现在我们需要知道它产生Java异常, 不知道HTTP,也不知道如何验证一个主体。 对应的ExceptionTranslationFilter负责这个服务, 特别负责返回错误代码403(如果主体已经通过授权, 但是权限不足 - 像上面的第七步), 或者启动一个AuthenticationEntryPoint(如果 主体还没有授权,因此我们会进入上面的第三步)。

5.4.2. AuthenticationEntryPoint

AuthenticationEntryPoint负责上面的步骤三。 像你想的那样,每个web应用会有一个默认的验证策略 (好,这可能像其他东西一样在Spring Security里配置, 但现在让我们保持简单)。每个主要的验证系统会有他们自己的 AuthenticationEntryPoint实现, 典型的执行一个动作,描述在第三步。

5.4.3. 验证机制

一旦你的浏览器提交了你的验证证书(像HTTP表单POST或者HTTP头) 这些需要一些服务器的东西保存这些权限信息。 但是现在我们进入上面的第六步。 在Spring Security中我们有一个特定的名称,为了收集验证信息的操作。 从一个用户代码中(通常是浏览器),引用它作为一个验证机制”。 例子是基于表单的登录和BASIC验证。 一旦验证细节被从用户代理处收集到, 一个Authentication 请求对象就会被建立,然后放到AuthenticationManager

在验证机制获得完全的 Authentication后,它会认为请求合法, 把Authentication放到 SecurityContextHolder里,然后让原始请求重试 (上面第七步)。如果,其他可能, AuthenticationManager拒绝了请求, 请求机制会让用户代理重试(上面第二步)。

5.4.4. 在请求之间保存SecurityContext

依照应用类型,这里需要一个策略, 在用户操作之间保存安全环境。在一个典型的web应用中, 一个用户日志,一次或顺序被它的session id。服务器缓存 主体信息在session整个过程中,在Spring Security中,保存 SecurityContext,从请求失败 SecurityContextPersistenceFilter,默认保存到 HttpSession里的一个属性,在HTTP请求之间。 它重新保存环境到SecurityContextHolder,为每个请求。 然后为每个请求清空SecurityContextHolder。 你不应该为了安全目的,直接操作HttpSession。 这里有简单的方法实现 - 一直使用SecurityContextHolder代替。

很多其他类型的应用(比如,一个无状态的REST web服务)不会使用 HTTP会话,会在每次请求时,重新验证。然而,这对 SecurityContextPersistenceFilter也很重要,确保包含在 SecurityContextHolder中,在每次请求后清空。

Note

在一个单一会话接收同步请求的应用里,相同的SecurityContext 实例会在线程之间共享。即使使用一个ThreadLocal,也是使用了来自 HttpSession的相同实例。如果你希望暂时改变一个线程的上下文 就会造成影响。如果你只是使用 SecurityContextHolder.getContext().setAuthentication(anAuthentication), 然后Authentication对象会反应到 所有并发线程,共享相同的 SecurityContext实例。你可以自定义 SecurityContextPersistenceFilter的行为来创建完全新的一个线程 避免影响其他的。还可以选择的是,你可以创建一个新实例,只在你暂时修改上下文的时候。 这个方法SecurityContextHolder.createEmptyContext()总会返回一个新的上下文实例。

5.5. Spring Security中的访问控制(验证)

主要接口,负责访问控制的决定,在Spring Security中是 AccessDecisionMananger。它有一个 decide方法,可以获得一个 Authentication对象。展示主体的请求权限, 一个secure object”(看下边)和一个安全元数据属性队列, 为对象提供了(比如一个角色列表,为访问被授予的请求)。

5.5.1. 安全和AOP建议

如果你熟悉AOP的话,就会知道有几种不同的拦截方式:之前,之后,抛异常和环绕。 其中环绕是非常有用的,因为advisor可以决定是否执行这个方法,是否修改返回的结果,是否抛出异常。 Spring Security为方法调用提供了一个环绕advice,就像web请求一样。 我们使用Spring的标准AOP支持制作了一个处理方法调用的环绕advice,我们使用标准filter建立了对web请求的环绕advice。

对那些不熟悉AOP的人,需要理解的关键问题是Spring Security可以帮助你保护方法的调用,就像保护web请求一样。 大多数人对保护服务层里的安全方法非常按兴趣。 这是因为在目前这一代J2EE程序里,服务器放了更多业务相关的逻辑(需要澄清,作者不建议这种设计方法,作为替代的,而是应该使用DTO,集会,门面和透明持久模式压缩领域对象,但是使用贫血领域对象是当前的主流思路,所以我们还是会在这里讨论它)。 如果你只是需要保护服务层的方法调用,Spring标准AOP平台(一般被称作AOP联盟)就够了。 如果你想直接保护领域对象,你会发现AspectJ非常值得考虑。

可以选择使用AspectJ还是Spring AOP处理方法验证,或者你可以选择使用filter处理web请求验证。 你可以不选,选择其中一个,选择两个,或者三个都选。 主流的应用是处理一些web请求验证,再结合一些在服务层里的Spring AOP方法调用验证。

5.5.2. 安全对象和AbstractSecurityInterceptor

所以,什么是secure object”? Spring Security使用应用任何对象,可以被安全控制(比如一个验证决定)提供到它上面。 最常见的例子是方法调用和web请求。

Spring Security支持的每个安全对象类型都有它自己的类型,它们都是AbstractSecurityInterceptor的子类。 很重要的是,如果主体是已经通过了验证,在AbstractSecurityInterceptor被调用的时候,SecurityContextHolder将会包含一个有效的Authentication

AbstractSecurityInterceptor提供了一套一致的工作流程,来处理对安全对象的请求,通常是:

  1. 查找当前请求里分配的"配置属性"。

  2. 把安全对象,当前的Authentication和配置属性,提交给AccessDecisionManager,来进行以此认证决定。

  3. 有可能在调用的过程中,对 Authentication 进行修改。

  4. 允许安全对象进行处理(假设访问被允许了)。

  5. 在调用返回的时候执行配置的AfterInvocationManager。 如果调用抛出了一个异常,AfterInvocationManager就不会执行。

 

5.5.2.1. 配置属性是什么?

一个"配置属性"可以看做是一个字符串,它对于 AbstractSecurityInterceptor使用的类是有特殊含义的。 它们通过框架中的ConfigAttribute接口表现。 它们可能是简单的角色名称或拥有更复杂的含义,这就与 AccessDecisionManager实现的先进程度有关了。 AbstractSecurityInterceptor和配置在一起的SecurityMetadataSource用来为一个安全对象搜索属性。 通常这个属性对用户是不可见的。 配置属性将以注解的方式设置在受保护方法上,或者作为受保护URL的访问属性。 比如,当我们查看一些像 <intercept-url pattern='/secure/**' access='ROLE_A,ROLE_B'/> 在命名空间介绍里,这就是在说这些配置属性 ROLE_A 和ROLE_B应用到web请求匹配到指定的模式中。实际上,使用默认的 AccessDecisionManager配置,这意味着任何人 拥有GrantedAuthority匹配任何这些两个属性中的一个 会被允许访问。严格意义上,他们只是树形,解释是基于 AccessDecisionManager实现的。 前缀ROLE_的使用标记了这些属性是角色,会被 Spring Security的RoleVoter处理。它只与基于角色的 AccessDecisionManager有关。我们会在 验证章节看到AccessDecisionManager是如何实现的。

5.5.2.2. RunAsManager

假设AccessDecisionManager决定允许执行这个请求,AbstractSecurityInterceptor会正常执行这个请求。 话虽如此,罕见情况下,用户可能需要把SecurityContextAuthentication换成另一个Authentication,通过访问RunAsManager。 这也许在,有原因,不常见的情况下有用,比如,服务层方法需要调用远程系统,表现不同的身份。 因为Spring Security自动传播安全身份,从一个服务器到另一个(假设你使用了配置好的RMI或者HttpInvoker远程调用协议客户端),就可以用到它了。

5.5.2.3. AfterInvocationManager

按照下面安全对象执行和返回的方式-可能意味着完全的方法调用或过滤器链的执行。 这种状态下AbstractSecurityInterceptor对有可能修改返回对象感兴趣。 你可能想让它发生,因为验证决定不能“关于如何在”一个安全对象调用。 高可插拔性,AbstractSecurityInterceptor通过控制AfterInvocationManager,实际上在需要的时候,修改对象。 这里类实际上可能替换对象,或者抛出异常,或者什么也不做。 调用后检查只在调用成功后执行。如果出现了异常, 附加检查就会被略过。

AbstractSecurityInterceptor和相关对象展示在 Figure 5.1, “关键"secure object"模型”中。

关键"secure object"模型

Figure 5.1. 关键"secure object"模型

 

5.5.2.4. 扩展安全对象模型

只有开发者才会关心使用全心的方法,进行拦截和验证请求,将直接使用安全方法。 比如,可能新建一个安全方法,控制对消息系统的权限。 安全需要的任何事情,也可以提供一种拦截的方法(好像AOP的环绕advice语法那样)有可能在安全对象里处理。 这样说的话,大多数Spring应用简单拥有三种当前支持的安全类型(AOP联盟的MethodInvocation,AspectJ JoinPoint和web请求FilterInterceptor)完全透明的。

5.6. 国际化

Spring Security支持异常信息的国际化,最终用户希望看到这些信息。 如果你的应用被设计成给讲英语的用户的,你不需要做任何事情, 因为默认情况下Spring Security的信息都是引用的。如果你需要支持其他语言。 你所需要做的事情都包含在这一章节中的。

所有的异常信息都支持国际化,包括验证失败和访问被拒绝的相关信息(授权失败)。 应该被开发者和系统开发者关注(包括不正确的属性,接口契约,使用非法构造方法, 开始时间校验,调试级日志)的异常和日志没有被国际化,而是使用英语硬编码到 Spring Security的代码中。

spring-security-core-xx.jar中,你可以找到 org.springframework.security包下,包含了一些 messages.properties文件,和其他版本的国际化文件一样,这应该引用到你的 ApplicationContext中,因为Spring Security的类 都实现了spring的MessageSourceAware接口, 期待的信息处理器会在application context启动的时候注入进来。 通常所有你需要做的就是在你的application context中注册一个bean来引用这些信息。 下面是一个例子:

<bean id="messageSource"
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
  <property name="basename" value="classpath:org/springframework/security/messages"/>
</bean>

messages.properties是按照标准资源束命名的, 里边包括了Spring security所使用的默认语言的信息。 默认的文件是英文的。

如果你想自定义messages.properties文件,或者支持其他语言, 你需要复制这个文件,正确的把它重新命名,再把它注册到bean定义中。 这个文件中并没有太多的信息。所以国际化应该不是很繁重的工作。 如果你国际化了这个文件,请考虑一下把你的工作和社区分享,通过记录一个JIRA任务 把你翻译的messages.properties版本作为一个附件发送上去。

为了获得对应的信息,Spring Security利用了Spring的国际化支持。 为了让它工作起来,你必须确认进入的请求的地区信息保存在Spring的 org.springframework.context.i18n.LocaleContextHolder中。 Spring MVC的DispatcherServlet可以自动为你的应用做这些事情。 ,但是因为Spring Security的过滤器在它之前调用, LocaleContextHolder需要在过滤器调用之前 被设置来获得正确的Locale。 你可以在一个自己的过滤器中坐这些事情(必须在web.xml中配置到Spring Security过滤器的前面), 或者你可以使用Spring的RequestContextFilter。 请参考Spring框架的文档来获得更多的信息,关于如何在Spring中使用国际化。

contacts” 实例应用中就是用了国际化信息。

 

posted @ 2019-11-27 17:28  DarJeely  阅读(297)  评论(0编辑  收藏  举报