2015-05-18 91 views
3

我和春天有个安全:)的Spring Security + LDAP + CustomLdapAuthoritiesPopulator +与rememberMe

一个小问题,什么是我的目标: 配置LDAP身份验证与自定义角色,从数据库中提取,并记住我的功能。

什么做:

  • LDAP验证:为AD用户OK
  • 自定义角色从数据库:OK
  • 记住我:失败

我的问题是: '记住我'工作正常,persistent_logins表创建成功,它存储令牌罚款。但是当用户返回到网站时,spring显示“未经授权”页面。

我认为这是因为'记住我'对我的自定义角色一无所知,并从LDAP中获取角色。

的问题是:如何告诉“记住我”通过我的CustomLdapAuthoritiesPopulator让角色?

我的applicationContext.xml

<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> 
    <constructor-arg value="ldap://ldap.forumsys.com:389"/> 
    <property name="userDn" value="cn=read-only-admin,dc=example,dc=com"/> 
    <property name="password" value="password"/> 
</bean> 

<bean name="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/> 
    <property name="url" value="jdbc:mysql://localhost:3306/db"/> 
    <property name="username" value="username"/> 
    <property name="password" value="password"/> 
</bean> 

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> 
    <constructor-arg> 
     <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> 
      <constructor-arg ref="contextSource"/> 
      <property name="userDnPatterns"> 
       <list> 
        <value>uid={0},dc=example,dc=com</value> 
       </list> 
      </property> 
     </bean> 
    </constructor-arg> 
    <constructor-arg> 
     <bean class="my.extra.CustomLdapAuthoritiesPopulator"/> 
    </constructor-arg> 
</bean> 

<bean id="tokenRepository" 
     class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl"> 
    <property name="createTableOnStartup" value="false"/> 
    <property name="dataSource" ref="myDataSource"/> 
</bean> 

<security:authentication-manager> 
    <security:authentication-provider ref="ldapAuthProvider"/> 
</security:authentication-manager> 

<security:http auto-config="true" use-expressions="true"> 
    <security:access-denied-handler error-page="/403"/> 
    <security:intercept-url pattern="/login*" access="permitAll()"/> 
    <security:intercept-url pattern="/favicon.ico" access="permitAll()"/> 
    <security:intercept-url pattern="/resources/**" access="permitAll()"/> 
    <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/> 
    <security:form-login login-page='/login' login-processing-url="/j_spring_security_check" 
         default-target-url="/" authentication-failure-url="/login?fail"/> 
    <security:remember-me key="_spring_security_remember_me" token-validity-seconds="14400" 
          token-repository-ref="tokenRepository" 
          user-service-ref="ldapUserService"/> 

</security:http> 

<security:ldap-user-service id="ldapUserService" server-ref="contextSource" 
          group-search-base="dc=example,dc=com" 
          group-search-filter="ou={0})" 
          user-search-base="dc=example,dc=com" 
          user-search-filter="uid={0}"/> 

在调试过程中,当用户返回,CustomLdapAuthoritiesPopulator不叫。 我添加了代码来检查用户的角色(在欢迎页面和自定义403页面上)。

Collection<? extends GrantedAuthority> roles = SecurityContextHolder.getContext().getAuthentication().getAuthorities(); 
roles.forEach(System.out::println); 

在用户登录后,欢迎页面显示 “ROLE_USER”, “ROLE_ADMIN”

用户返回后,403页显示 “”; (无)

+0

你得到了上述问题的答案?如果不是,请尝试我的回答并访问我提供的链接以获取有关*记住我认证*的更多信息。 – OO7

+0

尝试用我的答案中的新链接。 – OO7

+0

我解决了这个问题,稍后会发布答案。 – EatingPeopleIsFun

回答

1

Spring Docs Remember-Me

如果你使用不使用身份验证提供一个UserDetailsService(例如LDAP提供),那么它不会,除非你的工作还在您的应用程序上下文中有一个UserDetailsS​​ervice bean。

尝试在applicationContext.xml添加以下: -

添加RoleVoter。 From Spring Docs

如果任何ConfigAttribute.getAttribute()开头的前缀表示它是一个角色。默认的前缀字符串是ROLE_,但这可能会被覆盖为任何值。它也可能被设置为空,这意味着基本上任何属性都将被投票。如下面进一步描述的那样,空白前缀的效果可能不是很理想。

如果没有配置属性以角色前缀开始,则弃权投票。投票授予访问权限,如果有一个匹配的GrantedAuthorityConfigAttribute以角色前缀开头。如果没有将GrantedAuthority完全匹配到以角色前缀开头的ConfigAttribute,则拒绝访问。

所有比较和前缀区分大小写。

<bean id="roleVoter" 
    class="org.springframework.security.access.vote.RoleVoter" p:rolePrefix="" /> 

添加AuthenticatedVoter。从Soring文档

票数如果IS_AUTHENTICATED_FULLYIS_AUTHENTICATED_REMEMBEREDIS_AUTHENTICATED_ANONYMOUSLY一个ConfigAttribute.getAttribute()存在。该列表按照最严格的检查顺序进行最严格的检查。

将检查当前的身份验证以确定委托人是否具有特定级别的身份验证。 “完全”认证选项意味着用户被完全认证(即AuthenticationTrustResolver.isAnonymous(Authentication)为假,AuthenticationTrustResolver.isRememberMe(Authentication)为假)。 "REMEMBERED"将授予访问权限,如果委托人已通过记事本进行身份验证或完全通过身份验证。 "ANONYMOUSLY"将授予访问权限,如果委托人通过记住我进行身份验证,或匿名或通过完全身份验证。

所有比较和前缀区分大小写。

<bean id="authVoter" 
    class="org.springframework.security.access.vote.AuthenticatedVoter">    
</bean> 

现在,配置弹簧AccessDecisionManager,其利用上述两个选民,为了来确定用户是否被授予正确的权限访问资源

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.ConsensusBased"> 
    <property name="allowIfAllAbstainDecisions" value="false" /> 
    <property name="decisionVoters"> 
     <list> 
      <ref bean="roleVoter" /> 
      <ref bean="authVoter" /> 
     </list> 
    </property> 
</bean> 

详情:访问Spring security 3 remember-me with LDAP authentication


EDIT 1:

  1. Spring Security with LDAP and Database roles
  2. Spring Security 3.1 - Implement UserDetailsService with Spring Data JPA

编辑2:

下面源最初发布于Configuring Spring Security Form Login with Remember-Me Enabled

创建自定义RememberMeProcessingFilter:

public class MyRememberMeProcessingFilter extends RememberMeProcessingFilter { 
    private myService; 

    @Override 
    protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) { 
     // perform some custom logic when the user has been 'remembered' & authenticated - e.g. update a login count etc 
     this.myService.doSomeCustomBusinessLogic(authResult.getName()); 

     super.onSuccessfulAuthentication(request, response, authResult); 
    } 
} 

RememberMeProcessingFilter包括你希望当用户返回到您的网站,并通过该应用程序“记得”运行任何自定义业务逻辑。

不要忘了补充:

<custom-authentication-provider /> 

这肯定使该记得我实际上是作为身份验证提供 - 即当以前已经问过你的用户返回该记住的,这增加了记住我的提供者列表,检查用户是否被认证。

+0

提供的链接不包含任何与“LDAP记得我”实现相关的信息。我不知道如何将其应用于我的配置。此外,很多标记为“已弃用”的内容我正在使用Spring Security 3.2.4.RELEASE – EatingPeopleIsFun

+0

我已经更新了我的答案。请尝试此更新。 – OO7

+0

RoleVoter会替换我的'use-expressions = true',我不需要它,所有我的控制器的方法用@PreAuthorize('ExpressionHere')注释。我不想删除前缀'ROLE_'。我的主要问题是:我在登录后使用,它会从我的数据库为用户分配角色。但记住我对CustomLdapAuthoritiesPopulator一无所知,当用户返回时,它不能正确地分配我认为的角色。 – EatingPeopleIsFun

0

错解使用

<security:ldap-user-service/> 

如果你想从数据库应用自定义角色“记住我”的功能。

仍然需要实现自定义的UserDetailsS​​ervice并从记事本部分引用它。

<security:http> 
.... 
<security:remember-me key="_spring_security_remember_me" token-validity-seconds="14400" 
          token-repository-ref="tokenRepository" 
          user-service-ref="rememberMeUserDetailsService"/> 
</security:http> 

<bean id="rememberMeUserDetailsService" class="gpb.extra.RememberMeUserDetailsService"/> 

它有点棘手,但工作,我的数据库存储每个用户名,导致它存储角色。所以,我可以检查任何没有LDAP的用户。

package gpb.extra; 

import gpb.database.models.User; 
import gpb.database.utils.HibernateUtils; 
import org.springframework.security.core.GrantedAuthority; 
import org.springframework.security.core.authority.SimpleGrantedAuthority; 
import org.springframework.security.core.userdetails.UserDetails; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.core.userdetails.UsernameNotFoundException; 

import java.util.Arrays; 
import java.util.Collection; 
import java.util.HashSet; 
import java.util.List; 

public class RememberMeUserDetailsService implements UserDetailsService { 
@Override 
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 

    Collection<GrantedAuthority> authorities = new HashSet<>(); 
    List<User> users = HibernateUtils.get(User.class, username, "username"); 
    if (users.size() == 0) { 
     throw new UsernameNotFoundException("User not found"); 
    } else { 
     User existingUser = users.get(0); 
     if (!existingUser.getRoles().isEmpty()) { 
      List<String> roles = Arrays.asList(existingUser.getRoles().split(",")); 
      roles.forEach(t -> authorities.add(new SimpleGrantedAuthority(t.trim()))); 
     } 
    } 

    boolean enabled = true; 
    boolean accountNonExpired = true; 
    boolean credentialsNonExpired = true; 
    boolean accountNonLocked = true; 

    return new org.springframework.security.core.userdetails.User(
      username, "password", enabled, accountNonExpired, 
      credentialsNonExpired, accountNonLocked, authorities); 
} 
} 

代码演示主要思想。

非常感谢@ OO7指向我正确的方式和惊人的帮助:)

+0

我的荣幸。我很高兴知道这个问题已经解决。 +1问这样一个好问题。 – OO7

相关问题