`

Spring Security介绍(2)

阅读更多

7.2  验证用户身份

在为应用程序应用安全措施时,决定是否允许用户访问受保护资源之前首先需要判断用户的身份。在绝大多数应用程序中,这意味着为用户显示一个登录界面,并要求他们提供用户名和密码。

不同应用程序提示用户输入其用户名和密码的方式有所不同。现在,我们假设用户具体的登录信息已经提供,并且需要Spring Security验证当前用户的身份。在本章稍后,我们将介绍以不同方式提示用户输入其用户名和密码的内容。

在Spring Security中,认证管理器负责确定用户的身份。而认证管理器是由org.acegi- security.AuthenticationManager接口定义的:

 

第三次一定会有好运

啊哈!在AuthenticationManager的程序包名称中有acegi这个单词。正如本章较早前提及的那样,Spring Security原先被称为Acegi Security。在Acegi被正式更名为Spring Security时,其类的封装方式也会更改。实际上,这将是Acegi/Spring Security已经拥有的“第三垒”程序包名称(译者注:“第三垒”是指在菱形球场上由本垒逆时针数的第三个垒,是选手到本垒之前的最后一垒)。 Acegi最初被封装在net.sf.acegisecurity之下……接着它被更改为org.acegisecurity。在1.1.0版发布时,它 将很可能被重新封装在org.springframework.security之下。尽管如此,由于那些更改尚未发生,因此本章中的示例仍显示 org.acegisecurity封装方式。

这里的authenticate()方法将会尝试利用org.acegisecurity.Authentication对象(它带有相应的主体和 凭证)来验证用户身份。如果认证成功,authenticate()方法会返回一个完整的Authentication对象,其中包括用户已被授予的权限 信息(它们将由认证管理器使用)。如果认证失败,则它会抛出一个AuthenticationException。

正如你所看到的,AuthenticationManager接口非常简单,你可以相当轻松地执行自己的 AuthenticationManager。但是Spring Security提供有ProviderManager,那是一个适用于绝大多数情形的AuthenticationManager执行。所以,让我们看 一下如何使用ProviderManager,而不是讨论开发自己的认证管理器。

 

 

7.2.1  配置ProviderManager

ProviderManager是认证管理器的一个实现,它将验证身份的责任委托给一个或多个认证提供者,如图7.2所示。

  
(点击查看大图)图7.2  ProviderManager将身份验证的
职责委托给一个或多个认证提供者


ProviderManager的用途是使你能够根据多个身份管理源来认证用户。它不是依靠自己实现身份验证,而是逐一认证提供者的集合, 直到某一个认证提供者能够成功地验证该用户的身份(或者是已经尝试完了该集合中所有的认证提供者)。这使得Spring Security能够为单个应用程序提供多种认证机制。

下列XML程序块显示的是Spring配置文件中ProviderManager的一种典型配置:

 

通过ProviderManager的providers属性,可以为其提供认证提供者列表。通常你只需要一个认证提供者,但是在某些情况下,提供 由若干个认证提供者组成的列表会十分有用。在这种情况下,如果一个认证提供者验证身份失败,可以尝试另一个认证提供者。Spring提供了若干个认证提供 者,如表7.1所列:

表7.1   Spring Security提供的针对各种场合的认证提供者

认证提供者( org.acegisecurity.*

   

adapters.AuthByAdapterProvider

使用容器适配器验证身份。这使得验证在
Web
容器(举例来说, Tomcat JBoss
Jetty
Resin 等)内所创建的用户的身份成为可能。

providers.anonymous.AnonymousAuthenticationProvider

以匿名用户方式验证用户。在即使用户尚
未登录,仍需要用户令牌时,会比较有用。


续表

认证提供者( org.acegisecurity.*

   

providers.cas.CasAuthenticationProvider

根据 JA-SIG “中心认证服务”( CAS )验证
身份。在用户需要单点登录能力时会比较有用。

providers.dao.DaoAuthenticationProvider

从数据库中获取用户信息,包括用户名和密码。

providers.dao.LdapAuthenticationProvider

根据某一轻量级目录访问协议( LDAP )服
务器验证身份。

providers.jaas.JaasAuthenticationProvider

JAAS 登录配置中获取用户信息。

providers.rememberme.RememberMeAuthenticationProvider

验证某一之前验证过并且被记住的用户的身份。
这使得无需提示输入用户名和密码即自动登
录某一用户成为可能。

providers.rcp.RemoteAuthenticationProvider

根据远程服务验证用户身份。

providers.TestingAuthenticationProvider

用于单元测试。自动认为一个
TestingAuthenticationToken
是有效的。不应用
于生产环境。

providers.x509.X509AuthenticationProvider

使用 X.509 证书验证用户身份。对于验证实际上
是其他应用程序(比如,某一 Web 服务
客户端)的用户身份会比较有用。

runas.RunAsImplAuthenticationProvider

针对身份已经被运行身份管理器替换的用户进行认证。

正如你在表7.1中所看到的那样,Spring Security几乎为每一种需求都提供了一个认证提供者。但是,如果仍未能找到符合你的应用程序安全需求的认证提供者,则可以随时通过执行 org.acegisecurity.providers.AuthenticationProvider接口来创建你自己的认证提供者:

 

也许读者已经注意到,这个AuthenticationProvider接口与前几页显示的AuthenticationManager接口没有什 么太大不同,它们都有一个处理认证的authenticate()方法。实际上,可以把认证提供者看做是第二等级的认证管理器。

限于篇幅,本书将无法详细介绍Spring Security的所有11个认证提供者。不过,这里将集中介绍两个最常用的认证提供者,首先从DaoAuthenticationProvider开始,它支持进行简单的面向数据库的身份验证。

 

 

7.2.2  根据数据库验证身份

许多应用程序将包括用户名和密码在内的用户信息保存在关系数据库中。如果那就是你的应用程序保留用户信息的方式,那么你会发现,对于你的应用程序来说,选择Spring Security提供的DaoAuthenticationProvider可能会比较好。

DaoAuthenticationProvider是一个简单的认证提供者,它使用数据存取对象(DAO)来从关系数据库中检索用户信息(包括用户的密码)。


在取得了所需的用户名和密码之后,DaoAuthenticationProvider通过比较从数据库中检索到的用户名和密码以及来自认 证管理器的通过Authentication对象中传入的主体和凭证完成身份验证(如图7.3所示)。如果上述用户名和密码与主体和凭证相匹配,则用户通 过身份验证,同时返回给认证管理器一个已完全填充好的Authentication对象。否则会抛出一个 AuthenticationException,表明身份验证失败。

 
(点击查看大图)图7.3  DaoAuthenticationProvider
通过从数据库中获取用户信息帮助认证管理器进行身份验证

配置一个DaoAuthenticationProvider再简单不过了。下面这一段XML摘选显示了如何声明一个DaoAuthenticationProvider Bean,并且装配上它所依赖的DAO:

 

这里的userDetailsService属性被用来指定将用于从数据库中检索用户信息的那个Bean。这个属性期望 org.acegisecurity.userdetails.UserDetailsService的一个实例。剩下来的问题就是那个 userDetailsService Bean是如何配置的了。


这里的UserDetailsService接口要求只执行一个方法:

 


这个方法相当显而易见,而且读者可能已经在思考几种可以实现这个接口的方法了。但是在你开始 自行编写UserDetailsService的实现之前,你可能有兴趣知道Spring Security带有两个现成的可供选择的AuthenticationDao实现:InMemoryDaoImpl和JdbcDaoImpl。下面让我 们来看一看这两个类是如何查找用户详细信息的,首先从InMemoryDaoImpl开始。

 

 

7.2.2.1  使用内存DAO

尽管假定AuthenticationDao对象将总是通过查询关系数据库来获取用户信息,可能看上去是一种很自然的想法,但实际上并不是必须要那 样的。如果你的应用程序的身份验证需求是微不足道的,或者是为了开发期间的方便起见,也许更简单的做法是直接在Spring配置文件中配置你的用户信息。

为此,Spring Security提供了InMemoryDaoImpl,那是一个从Spring配置文件中获取用户信息的UserDetailsService实现。这里是你可能如何在Spring配置文件中配置InMemoryDaoImpl的一个示例:

 
 

这里的userMap属性通过一个org.acegisecurity.userdetails.memory.UserMap对象来定义一组用户 名、密码和权限。幸运的是,在装配InMemoryDaoImpl时,你不必为构建一个UserMap实例而操心,因为Spring Security提供有一个属性编辑器,它能够帮你把一个String转化为一个UserMap对象。

在这个userMap的每一行上,String都是一个名字-值对,其中名字就是用户名,值是一个由逗号分隔的列表,它以相应用户的密码开头,后面跟着一个或多个准备授予该用户的权限的名称。图7.4分解开了上述用户映像中的一个条目格式。

 
(点击查看大图)图7.4  Spring Security用户映像中一
个用户名对应一个密码、授予的权限以及它们的状态(可选)


在前面的authenticationDao Bean声明中,定义了四个用户:palmerd、bauerj、obrianc和myersn。他们的密码分别是4moreyears、ineedsleep、nosmile和traitor。各用户的权限授予情况如下:

用户名为palmerd的用户已被给予ROLE_PRESIDENT权限;

bauerj被给予ROLE_FIELD_OPS;

myersn被给予ROLE_CENTRAL_OPS;

obrianc用户被授予两个权限:ROLE_SR_ANALYST和ROLE_OPS。

需要特别注意这里的palmerd和myersn。在他们的密码之后,紧接着一个特殊的disabled标志,表明他们已经被禁用(因此无法验证身份)。

尽管InMemoryDaoImpl既方便又简单,但是它有一些显而易见的局限性。主要是,对安全性进行管理时要求你编辑Spring配置文件并且 重新部署应用。虽然在开发环境下这是可以接受的(而且可能还是有帮助的),但是对于生产用途而言这种做法就太笨拙了。因此,本书作者强烈反对在生产环境下 使用InMemoryDaoImpl。相反,你应该考虑使用我们接下来将介绍的JdbcDaoImpl。

 

 

7.2.2.2  声明一个JDBC DAO

JdbcDaoImpl是一个简单而灵活的认证DAO,它从关系数据库中检索用户的信息。在它最简单的形式中,只需要一个javax.sql.DataSource对象的引用,而且它可以通过以下方式在Spring配置文件中进行声明:

 

正如这里配置的那样,JdbcDaoImpl对用户信息在相应数据库中的存储情况做了一些基本的假设。特别是,它假设有一张“Users”表和一张“Authorities”表,如图7.5所示。

  
图7.5  JdbcDaoImpl假设的数据库表


当JdbcDaoImpl查找用户信息时,它会使用下列SQL作为查询语句:

 

类似地,当查找某个用户的授权时,JdbcDaoImpl会使用下列SQL:

  

尽管JdbcDaoImpl假定的表结构非常直接,它们很可能与你已经为自己的应用程序安全建立的表结构不一致。比如,在RoadRantz应用程 序中,Motorist表保存已注册用户的用户名(在email列中)和密码。这是否意味着我们无法在RoadRantz应用程序中使用 JdbcDaoImpl来验证驾车者的身份呢?

当然不是。但是如果准备使用JdbcDaoImpl,我们就必须费点力气通过设置usersBy- UserNameQuery属性来告诉它如何找到用户信息。以下对authenticationDao Bean的调整将使得它能够从RoadRantz的Motorist表中查询用户:

  

现在,JdbcDaoImpl已经知道到Motorist表中查找认证信息了。但是,我们还必须告诉JdbcDaoImpl如何在相应数据库中查询某一用户的授权。为此,我们将设置authoritiesByUsernameQuery属性:

  


到这里,我们已经配置好JdbcDaoImpl从Motorist_Privileges表 中检索驾车者的授权。这里的查询还加入了Motorist表,那是因为Motorist_Privileges表只是通过一个外关键字来了解某个 Motorist的,而JdbcDaoImpl期望这个查询能通过用户名检索权限。

 

 

7.2.2.3  使用加密的密码

当DaoAuthenticationProvider将用户在身份验证时提供的密码与从数据库中检索到密码相比较时,它假设一直存储着的那个密码 是没有加密的。为了增强安全性,你可能会希望在将那个密码存储到数据库中之前对它进行加密。但是,如果那个密码以加密形式存储在数据库中,那么用户提供的 密码必须也要加密,只有这样,这两个密码才能相互比较。

为了适应加密的密码,DaoAuthenticationProvider可以装配一个密码编码器。Spring Security带有几个密码编码器可供选择,如表7.2所示。

表7.2   Spring Security的密码编码器

密码编码器( org.acegisecurity.providers.*

   

encoding.Md5PasswordEncoder

在密码上执行“信息摘要”( MD5 )编码

encoding.PlaintextPasswordEncoder

在密码上不执行任何编码,照原样返回它

encoding.ShaPasswordEncoder

在密码上执行“安全散列算法”( SHA )编码

ldap.authenticator.LdapShaPasswordEncoder

使用 LDAP SHA salted-SHA SSHA )编码

技术编码密码

在默认情况下,DaoAuthenticationProvider使用PlaintextPasswordEncoder,那意味着密码仍然没有 被编码。但是,我们可以通过设置DaoAuthenticationProvider的passwordEncoder属性来指定另一种编码方法。举例来 说,要让DaoAuthenticationProvider使用MD5编码技术,可以加入以下代码:

 

你还将需要为编码器设置一个种子源(salt source)。一个种子源为所用的编码技术提供种子(salt),或者称加密密钥。Spring Security提供有两个种子源:

SystemWideSaltSource——对所有用户提供相同的种子;

ReflectionSaltSource——利用用户的User对象中某个指定属性的反射来生成种子。

在这两个种子源中,ReflectionSaltSource更加安全,因为每一个用户的密码很可能会使用不同的种子值来编码。即使假设有黑客推测 出了用来编码某个用户密码的种子,他们也不太可能使用同一个种子破解开另一个用户的密码。要想使用一个ReflectionSaltSource,可以通 过如下方式将它装配到DaoAuthenticationProvider的saltSource属性中:

 
 

在这里,用户的userName属性被用作种子来加密用户的密码。要特别值得重视的是,必须保证该种子是静态的,而且永远不会改变;否则,就再也不可能对这个用户身份进行验证了(除非是在使用新的种子更改之后,那个密码又被重新编码回来了)。

尽管ReflectionSaltSource的确是更加安全,但是SystemWideSaltSource更加简单,而且对于绝大多数情况来说已经足够。
SystemWideSaltSource使用单个种子值加密所有用户的密码。要想使用一个SystemWideSaltSource,可以通过如下方式装配saltSource属性:

 

在这里,相同的种子值ABC123XYZ789被用于加密所有密码。

 

 

7.2.2.4  缓存用户信息

每次当请求一个受保护的资源时,认证管理器就被调用以检索用户的安全信息。但是如果检索用户信息涉及到要查询数据库,那么每次都查询相同的数据就可 能会妨碍应用性能。考虑到用户的信息一般不会频繁改变,因此也许更好的做法是根据第一次查询缓存那些用户数据,然后在后续的每次查询中从缓存中检索用户信 息。

为了启用用户信息的缓存功能,我们必须给DaoAuthenticationProvider提供org.acegisecurity.providers.dao.UserCache接口的一个实现。这个接口批准三个方法的执行:

 

UserCache中的这几个方法都无需加以说明,它们提供向缓存中放入、检索或删除用户明细信息的功能。这样以来,编写你自己的 UserCache实现就应该相当简单了。然而,在你考虑开发自己的UserCache实现之前,应该首先考虑Spring Security提供的两个方便的实现:

org.acegisecurity.providers.dao.cache.NullUserCache

org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache

事实上,NullUserCache并不执行任何缓存行为。相反,它每次都是从它的getUserFromCache方法返回null,促使 DaoAuthenticationProvider来查询用户信息。这是DaoAuthenticationProvider使用的默认 UserCache实现。

EhCacheBasedUserCache是一个更加有用的缓存实现。顾名思义,它是基于EHCache实现的。和 DaoAuthenticationProvider一起使用EHCache是很简单的,只需要将一个EhCacheBasedUserCase Bean装配进DaoAuthenticationProvider的userCache属性即可:

 

这里的cache属性引用一个ehcache Bean,后者应该是一个EHCache Cache对象。得到那样一个Cache对象的一种途径是使用Spring Modules的缓存模块。举例来说,下面这段XML就使用Spring Modules来配置EHCache:

 

读者可能还记得本书第5章的内容,Spring Modules的EhCacheFactoryBean是一种Spring Factory Bean,它生成一个EHCache Cache对象。实际的缓存配置是在ehcache.xml文件中找到的,它将会从类途径中检索到。

当你的应用程序的安全信息保存在某一关系数据库中时,DaoAuthenticationProvider将非常棒。不过,一个应用程序的安全性经 常构建成根据一个LDAP服务器进行身份验证。下面让我们来看一下如何使用Spring Security的LdapAuthenticationProvider,在必须通过LDAP进行身份验证时,它是更加适用的一种选择。

 

 

7.2.3  根据LDAP仓库进行身份验证

Spring Security支持通过LdapAuthenticationProvider根据LDAP进行身份验 证,LdapAuthenticationProvider是一个知道如何根据LDAP仓库查看用户凭证的认证提供者。下列<bean>举例 说明的是针对LdapAuthenticationProvider的一种典型配置:

 

正如读者可以看到的那样,LdapAuthenticationProvider并没有太多令人兴奋的地方,那儿没有有关如何找到相应LDAP服务 器或仓库初始上下文的具体信息。相反,LdapAuthenticationProvider通过构造函数注入,与一个authenticator和一个 populator装配在一起。那些Bean是什么,它们是用来干什么的?

事实上,尽管LdapAuthenticationProvider声称知道如何与一个LDAP仓库对话,但它实际上依赖两个策略对象来做真正的工作:

authenticator策略根据LDAP仓库处理实际的身份验证(举例来说,验证用户凭证)。Authenticator策略可以是任何实现org.acegisecurity.providers.ldap.Ldap Authenticator的对象。

populator策略负责从LDAP仓库检索用户获得的权限集。Populator策略是任何实现org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator的对象。

由于认证和权限责任被定义为策略,从LdapAuthenticationProvider分离出来了,你就能够在最适合你的应用程序安全需求的策略实现中进行装配了。

因此,剩下的问题就是这authenticator和populator Bean是如何定义的了。下面让我们考察authenticator Bean开始,它为LdapAuthenticationProvider定义认证策略。

 

 

7.2.3.1  利用LDAP绑定进行身份验证

在要根据LDAP进行身份验证时,通常会采取以下两种方法:

利用一个LDAP用户的用户名和密码绑定到LDAP服务器;

在LDAP中检索一个用户的条目,然后将提供的密码和检索到的LDAP记录中的密码属性相比较。

为了绑定身份验证,Spring Security带有一个称为BindAuthenticator的LdapAuthenticator实现。BindAuthenticator使用一 个LDAP bind运算符来把一个用户绑定到LDAP服务器。这种方法依靠LDAP服务器来验证所绑定用户的凭证。

下列<bean>在Spring中声明一个BindAuthenticator:

 

在这里,我们已经声明这个BindAuthenticator是通过一个构造函数参数和通过userDnPatterns属性来注入的。稍后我们将回到这个构造函数参数。首先,让我们考虑那个userDnPatterns属性。

这个userDnPatterns属性是用来告诉BindAuthenticator如何在LDAP中找到某个用户的。它的值是一个模式列表,是 BindAuthenticator将用做识别名(DN)来识别用户的一个或多个模式。在这里,我们只使用一个DN模式,如图7.6所示。

  
图7.6  出于我们的目的,一个用户的识别名
(DN)被分解为用户ID(UID)和组织单元(OU)


DN模式中的{0}是一个充当用户名占位符的模式参数。举例来说,如果用户名为cwagon,那么用来绑定到LDAP的DN将是uid=cwagon,ou=motorists。

现在,回到前面那个构造函数参数。为了能够做好自己的工作,BindAuthenticator需要知道的主要事情是如何访问相应的LDAP仓库。因此,它和一个构造函数参数一同构建,装配到initialDirContextFactory,其声明方式如下:

  

DefaultInitialDirContextFactory捕获连接到一台LDAP服务器所需的全部信息,并生成一个JNDI DirContext对象。如果读者不太了解JNDI或DirContext,则不必担心这些具体细节,只需记住BindAuthenticator是利 用DefaultInitialDirContextFactory来获悉如何到达相应的LDAP仓库即可。

用来创建DefaultInitialDirContextFactory的那个构造函数参数和LDAP提供者的URL一起装配。在这里,我们已经 把它和对RoadRantz LDAP服务器 的一个引用装配在一起,并且在dc=roadrantz,dc=com处建立了初始上下文。用来查找用户信息的DN将被关系到这个初始上下文。

 

 

7.2.3.2  通过比较密码进行身份验证

做为绑定认证之外的一种可选方式,Spring Security还支持通过使用PasswordComp- arisonAuthenticator的密码比较进行身份验证。PasswordComparisonAuthenticator通过比较提供的密码和 用户记录中的一个密码属性(在默认情况下是userPassword)进行工作。它在Spring中的配置可能如下所示:

 

注意,除了类名称外,这个PasswordComparisonAuthenticator声明与前面的BindAuth- enticator声明完全相同。那是因为在它们的最简单格式中,两者在根本上是相同的。它们都需要一个初始化上下文工厂来知道如何到达相应的LDAP仓 库,而且都需要一个或多个DN模式来定位用户记录。

但是,还有一些属性可以用来自定义PasswordComparisonAuthenticator。举例来说,如果默认的 userPassword属性并不符合你的需求,则可以通过装配一个新的值到passwordAttributeName属性来覆盖它。比如说,按如下方 式声明PasswordComparison- Authenticator,以对照名为userCredentials的属性比较所提供的密码:

 
 

你可能选择的另一个自定义内容是密码如何在LDAP中编码。在默认情况下,PasswordComparisonAuthenticator使用 Spring Security的LdapShaPasswordEncoder在比较之前编码密码。LdapShaPasswordEncoder支持LDAP“安全 散列算法”(SHA)和SSHA(salted-SHA)编码技术。但是如果这些不符合你的需求,任何一个org.acegisecurity. providers.encoding.PasswordEncoder实现(包括表7.2中的那些)都可以被装配进passwordEncoder属性 中。

举例来说,如果需要在LDAP中以纯文本方式存储密码(不建议这样,但是可能有这样的情况),那么可以像下面这样声明PasswordComparisonAuthenticator:

 

在转而研究populator策略Bean之前,让我们再最后调整一下initialDirContextFactory bean。

与BindAuthenticator不同,PasswordComparisonAuthenticator并不利用用户的DN绑定到LDAP。 有些LDAP提供者允许匿名绑定,在那种情况下,initialDirContextFactory将会照常工作。不过,出于安全考虑,绝大多数LDAP 提供者并不允许匿名绑定,因此我们将需要为DefaultInitialDirContextFactory提供一个管理器DN和密码来绑定:

  
 

当DefaultInitialDirContextFactory访问LDAP时,它会绑定为那个管理器,并且在比较用户的密码时代表该用户。

现在,让我们来配置populator策略Bean,以完成整个LDAP身份验证内容。

 

 

7.2.3.3  声明populator策略Bean

验证用户身份只是LdapAuthenticationProvider执行的第一步。一旦用户的身份被确定,LdapAuthenticationProvider必须检索该用户被授予的权限列表,以确定该用户在当前应用程序中具有哪些权力。

如同身份验证的情况一样,LdapAuthenticatorProvider使用一个策略对象来从LDAP查找用户被授予的权限。Spring Security带有一个LdapAuthoritiesPopulator接口的实 现:DefaultLdapAuthoritiesPopulator。下面是DefaultLdapAuthoritiesPopulator在 Spring中的配置情况:

 

读者首先会注意到的是,DefaultLdapAuthoritiesPopulator的构建中包含两个构造函数参数,其中第一个是对我们的老朋 友initialDirContextFactory的一个引用。几乎和authenticator策略Bean一样,populator策略Bean需 要知道如何到达相应的LDAP仓库,以检索用户被授予的权限。

第二个构造函数参数帮助DefaultLdapAuthoritiesPopulator在LDAP仓库中查找组。由于LDAP仓库实际上是分层 的,安全组随处可见。这个构造函数参数指定一个基础DN,根据它来搜索组。这个基础DN与初始上下文相关。因此,对于组基础DN为“ou=groups” 的情况,我们将搜索“ou=groups,dc=roadrantz,dc=com”中的组。

最后,这里的groupRoleAttribute属性指定将包含角色信息(它们实际上能被转化成用户被授予的权限)的特征名称。它的默认值为cn,但是在这个示例中,我们把它设置成了ou。
这样配置之后,DefaultLdapAuthoritiesPopulator就会检索到所有包含指定用户的组——更确切地说,应该是所有member属性为该用户DN的组。


举例来说,假设你有一个LDAP仓库,填充有下列LDIF:

 
 

当名为craig的用户通过身份验证时,他获得的权限将包括ROLE_MOTORIST和ROLE_VIP。但是,当raymie通过身份验证时,她获得的权限将只包括ROLE_MOTORIST,因为vips组的member属性中并没有她的DN。

注意,这里的组名称(在ou属性中)被变换成大写字母,然后再加上前缀ROLE_。这里将大小写归一化,只是为了方便帮助找到用户的权限,而不必管它是小写的还是大写的。你可以通过把convertToUpperCase属性设置为false来关闭这一行为。

这里的ROLE_前缀是为了RoleVoter而提供的,我们将在7.3.2节中讨论它。如果你更希望使用另一个不同的角色前缀,则可以根据自己的喜好任意配置DefaultLdap- AuthoritiesPopulator的rolePrefix属性。

举例来说,要想关闭大写字母归一化,并且将角色前缀更改为GROUP_,则可以像下面那样配置DefaultLdapAuthoritiesPopulator:

 

你可能希望对DefaultLdapAuthoritiesPopulator做的再一个调整就是更 改它查找成员的方式。一般来说,它查找member属性含有所需用户DN的组。如果你的LDAP被设置成如此使用member属性,那么一切OK。但是, 让我们假设你的LDAP仓库使用一个associate属性(而不是member属性)跟踪成员资格。在那种情况下,你将希望像下面那样设置 DefaultLdapAuthoritiesPopulator的groupSearchFilter属性:

 

请注意,这里的groupSearchFilter属性使用{0}模式参数表示用户的DN。

现在,我们已经学会了如何使用Spring Security的身份验证处理Bean来识别用户。接下来,让我们看一下Spring Security是如何确定一个已通过验证的用户是否拥有访问受保护资源的适当权限的。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics