Spring Security(三十二):10. Core Services

Now that we have a high-level overview of the Spring Security architecture and its core classes, let’s take a closer look at one or two of the core interfaces and their implementations, in particular the AuthenticationManagerUserDetailsService and the AccessDecisionManager. These crop up regularly throughout the remainder of this document so it’s important you know how they are configured and how they operate.

现在我们对Spring Security体系结构及其核心类进行了高级概述,让我们仔细研究一个或两个核心接口及其实现,特别是AuthenticationManager,UserDetailsS​​ervice和AccessDecisionManager。这些文件会在本文档的其余部分定期出现,因此了解它们的配置方式以及它们的运行方式非常重要。

10.1 The AuthenticationManager, ProviderManager and AuthenticationProvider

The AuthenticationManager is just an interface, so the implementation can be anything we choose, but how does it work in practice? What if we need to check multiple authentication databases or a combination of different authentication services such as a database and an LDAP server?

AuthenticationManager只是一个接口,因此实现可以是我们选择的任何东西,但它在实践中如何工作?如果我们需要检查多个身份验证数据库或不同身份验证服务(如数据库和LDAP服务器)的组合,该怎么办?
 
The default implementation in Spring Security is called ProviderManager and rather than handling the authentication request itself, it delegates to a list of configured AuthenticationProvider s, each of which is queried in turn to see if it can perform the authentication. Each provider will either throw an exception or return a fully populated Authentication object. Remember our good friends, UserDetails and UserDetailsService? If not, head back to the previous chapter and refresh your memory. The most common approach to verifying an authentication request is to load the corresponding UserDetails and check the loaded password against the one that has been entered by the user. This is the approach used by the DaoAuthenticationProvider (see below). The loaded UserDetails object - and particularly the GrantedAuthority s it contains - will be used when building the fully populated Authentication object which is returned from a successful authentication and stored in the SecurityContext.
Spring Security中的默认实现称为ProviderManager,它不是处理身份验证请求本身,而是委托给已配置的AuthenticationProvider列表,每个查询器依次查询它们是否可以执行身份验证。每个提供程序将抛出异​​常或返回完全填充的Authentication对象。还记得我们的好朋友,UserDetails和UserDetailsS​​ervice吗?如果没有,请回到上一章并刷新记忆。验证身份验证请求的最常用方法是加载相应的UserDetails并检查加载的密码与用户输入的密码。这是DaoAuthenticationProvider使用的方法(见下文)。加载的UserDetails对象 - 特别是它包含的GrantedAuthority - 将在构建完全填充的Authentication对象时使用,该对象从成功的身份验证返回并存储在SecurityContext中。
 
If you are using the namespace, an instance of ProviderManager is created and maintained internally, and you add providers to it by using the namespace authentication provider elements (see the namespace chapter). In this case, you should not declare a ProviderManager bean in your application context. However, if you are not using the namespace then you would declare it like so:
如果您正在使用命名空间,则会在内部创建和维护ProviderManager的实例,并使用命名空间身份验证提供程序元素向其添加提供程序(请参阅命名空间章节)。在这种情况下,您不应在应用程序上下文中声明ProviderManager bean。但是,如果您没有使用命名空间,那么您将声明它如下:
 
<bean id="authenticationManager"
		class="org.springframework.security.authentication.ProviderManager">
	<constructor-arg>
		<list>
			<ref local="daoAuthenticationProvider"/>
			<ref local="anonymousAuthenticationProvider"/>
			<ref local="ldapAuthenticationProvider"/>
		</list>
	</constructor-arg>
</bean>

In the above example we have three providers. They are tried in the order shown (which is implied by the use of a List), with each provider able to attempt authentication, or skip authentication by simply returning null. If all implementations return null, the ProviderManager will throw a ProviderNotFoundException. If you’re interested in learning more about chaining providers, please refer to the ProviderManager Javadoc.

在上面的例子中,我们有三个提供者。它们按所示顺序(使用List暗示)进行尝试,每个提供程序都可以尝试进行身份验证,或者通过简单地返回null来跳过身份验证。如果所有实现都返回null,则ProviderManager将抛出ProviderNotFoundException。如果您有兴趣了解有关链接提供程序的更多信息,请参阅ProviderManager Javadoc。
 
Authentication mechanisms such as a web form-login processing filter are injected with a reference to the ProviderManager and will call it to handle their authentication requests. The providers you require will sometimes be interchangeable with the authentication mechanisms, while at other times they will depend on a specific authentication mechanism. For example, DaoAuthenticationProvider and LdapAuthenticationProvider are compatible with any mechanism which submits a simple username/password authentication request and so will work with form-based logins or HTTP Basic authentication. 
诸如Web表单登录处理过滤器之类的身份验证机制将引用ProviderManager,并将其调用以处理其身份验证请求。您需要的提供程序有时可以与身份验证机制互换,而在其他时候,它们将依赖于特定的身份验证机制。例如,DaoAuthenticationProvider和LdapAuthenticationProvider与提交简单用户名/密码身份验证请求的任何机制兼容,因此可以使用基于表单的登录或HTTP基本身份验证。
 
On the other hand, some authentication mechanisms create an authentication request object which can only be interpreted by a single type of AuthenticationProvider. An example of this would be JA-SIG CAS, which uses the notion of a service ticket and so can therefore only be authenticated by a CasAuthenticationProvider. You needn’t be too concerned about this, because if you forget to register a suitable provider, you’ll simply receive a ProviderNotFoundException when an attempt to authenticate is made.
另一方面,某些身份验证机制会创建一个身份验证请求对象,该对象只能由单一类型的AuthenticationProvider进行解释。这方面的一个例子是JA-SIG CAS,它使用服务票据的概念,因此只能由CasAuthenticationProvider进行身份验证。您不必过于担心这一点,因为如果您忘记注册合适的提供程序,那么在尝试进行身份验证时,您只会收到ProviderNotFoundException。

10.1.1 Erasing Credentials on Successful Authentication

By default (from Spring Security 3.1 onwards) the ProviderManager will attempt to clear any sensitive credentials information from the Authentication object which is returned by a successful authentication request. This prevents information like passwords being retained longer than necessary.

默认情况下(从Spring Security 3.1起),ProviderManager将尝试从Authentication对象中清除任何敏感凭证信息,该信息由成功的身份验证请求返回。这可以防止密码保留的时间超过必要的时间。
 
This may cause issues when you are using a cache of user objects, for example, to improve performance in a stateless application. If the Authentication contains a reference to an object in the cache (such as a UserDetails instance) and this has its credentials removed, then it will no longer be possible to authenticate against the cached value. You need to take this into account if you are using a cache. An obvious solution is to make a copy of the object first, either in the cache implementation or in the AuthenticationProvider which creates the returned Authentication object. Alternatively, you can disable the eraseCredentialsAfterAuthenticationproperty on ProviderManager. See the Javadoc for more information.
当您使用用户对象的缓存时,这可能会导致问题,例如,提高无状态应用程序的性能。如果身份验证包含对缓存中对象的引用(例如UserDetails实例)并且已删除其凭据,则将无法再对缓存的值进行身份验证。如果使用缓存,则需要考虑这一点。一个显而易见的解决方案是首先在缓存实现中或在创建返回的Authentication对象的AuthenticationProvider中制作对象的副本。或者,您可以在ProviderManager上禁用eraseCredentialsAfterAuthentication属性。有关更多信息,请参阅Javadoc。
 

10.1.2 DaoAuthenticationProvider

The simplest AuthenticationProvider implemented by Spring Security is DaoAuthenticationProvider, which is also one of the earliest supported by the framework. It leverages a UserDetailsService (as a DAO) in order to lookup the username, password and GrantedAuthority s. It authenticates the user simply by comparing the password submitted in a UsernamePasswordAuthenticationToken against the one loaded by the UserDetailsService. Configuring the provider is quite simple:

Spring Security实现的最简单的AuthenticationProvider是DaoAuthenticationProvider,它也是该框架最早支持的之一。它利用UserDetailsS​​ervice(作为DAO)来查找用户名,密码和GrantedAuthority。它只是通过将UsernamePasswordAuthenticationToken中提交的密码与UserDetailsS​​ervice加载的密码进行比较来对用户进行身份验证。配置提供程序非常简单:
 
<bean id="daoAuthenticationProvider"
	class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>

The PasswordEncoder is optional. A PasswordEncoder provides encoding and decoding of passwords presented in the UserDetails object that is returned from the configured UserDetailsService. This will be discussed in more detail below.

PasswordEncoder是可选的。 PasswordEncoder提供从配置的UserDetailsS​​ervice返回的UserDetails对象中显示的密码的编码和解码。这将在下面更详细地讨论。

10.2 UserDetailsService Implementations

As mentioned in the earlier in this reference guide, most authentication providers take advantage of the UserDetails and UserDetailsService interfaces. Recall that the contract for UserDetailsService is a single method:

如本参考指南前面所述,大多数身份验证提供程序都利用UserDetails和UserDetailsS​​ervice接口。回想一下UserDetailsS​​ervice的合同是一个单一的方法:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

The returned UserDetails is an interface that provides getters that guarantee non-null provision of authentication information such as the username, password, granted authorities and whether the user account is enabled or disabled. Most authentication providers will use a UserDetailsService, even if the username and password are not actually used as part of the authentication decision. They may use the returned UserDetails object just for its GrantedAuthority information, because some other system (like LDAP or X.509 or CAS etc) has undertaken the responsibility of actually validating the credentials.

返回的UserDetails是一个接口,提供保证非空提供身份验证信息的getter,例如用户名,密码,授予的权限以及是启用还是禁用用户帐户。大多数身份验证提供程序将使用UserDetailsS​​ervice,即使用户名和密码实际上未用作身份验证决策的一部分。他们可能仅仅为了GrantedAuthority信息使用返回的UserDetails对象,因为其他一些系统(如LDAP或X.509或CAS等)承担了实际验证凭据的责任。
 
Given UserDetailsService is so simple to implement, it should be easy for users to retrieve authentication information using a persistence strategy of their choice. Having said that, Spring Security does include a couple of useful base implementations, which we’ll look at below.
鉴于UserDetailsS​​ervice实现起来非常简单,用户应该可以使用自己选择的持久性策略轻松检索身份验证信息。话虽如此,Spring Security确实包含了一些有用的基础实现,我们将在下面介绍。

10.2.1 In-Memory Authentication

Is easy to use create a custom UserDetailsService implementation that extracts information from a persistence engine of choice, but many applications do not require such complexity. This is particularly true if you’re building a prototype application or just starting integrating Spring Security, when you don’t really want to spend time configuring databases or writing UserDetailsService implementations. For this sort of situation, a simple option is to use the user-service element from the security namespace:

易于使用创建自定义UserDetailsS​​ervice实现,从实际选择的持久性引擎中提取信息,但许多应用程序不需要这样的复杂性。如果您正在构建原型应用程序或刚刚开始集成Spring Security,而您真的不想花时间配置数据库或编写UserDetailsS​​ervice实现,则尤其如此。对于这种情况,一个简单的选择是使用安全命名空间中的user-service元素:
 
<user-service id="userDetailsService">
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>

This also supports the use of an external properties file:

这也支持使用外部属性文件:
 
<user-service id="userDetailsService" properties="users.properties"/>

The properties file should contain entries in the form

属性文件应包含表单中的条目
username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]

For example

jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
bob=bobspassword,ROLE_USER,enabled

10.2.2 JdbcDaoImpl

Spring Security also includes a UserDetailsService that can obtain authentication information from a JDBC data source. Internally Spring JDBC is used, so it avoids the complexity of a fully-featured object relational mapper (ORM) just to store user details. If your application does use an ORM tool, you might prefer to write a custom UserDetailsService to reuse the mapping files you’ve probably already created. Returning to JdbcDaoImpl, an example configuration is shown below:

Spring Security还包括一个UserDetailsS​​ervice,它可以从JDBC数据源获取身份验证信息。内部使用Spring JDBC,因此它避免了全功能对象关系映射器(ORM)的复杂性,只是为了存储用户详细信息。如果您的应用程序确实使用ORM工具,您可能更愿意编写自定义UserDetailsS​​ervice来重用您可能已经创建的映射文件。返回JdbcDaoImpl,示例配置如下所示:
 
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>

<bean id="userDetailsService"
	class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>

You can use different relational database management systems by modifying the DriverManagerDataSource shown above. You can also use a global data source obtained from JNDI, as with any other Spring configuration.

您可以通过修改上面显示的DriverManagerDataSource来使用不同的关系数据库管理系统。您还可以使用从JNDI获取的全局数据源,与任何其他Spring配置一样。

Authority Groups

By default, JdbcDaoImpl loads the authorities for a single user with the assumption that the authorities are mapped directly to users (see the database schema appendix). An alternative approach is to partition the authorities into groups and assign groups to the user. Some people prefer this approach as a means of administering user rights. See the JdbcDaoImpl Javadoc for more information on how to enable the use of group authorities. The group schema is also included in the appendix.

默认情况下,JdbcDaoImpl为单个用户加载权限,并假设权限直接映射到用户(请参阅数据库模式附录)。另一种方法是将权限划分为组并将组分配给用户。有些人更喜欢这种方法来管理用户权利。有关如何启用组权限的更多信息,请参阅JdbcDaoImpl Javadoc。组架构也包含在附录中。
posted @ 2018-12-19 13:48  帅LOVE俊  阅读(200)  评论(0编辑  收藏  举报