2015-05-18 2 views
3

У меня есть небольшая проблема с пружинной безопасностью :)Spring Security + LDAP + CustomLdapAuthoritiesPopulator + RememberMe

Какова моя цель: Настройки LDAP аутентификации с помощью пользовательских ролей, принесенных из базы данных, и запомнить функциональность.

Что сделано:

  • LDAP Auth: OK
  • пользовательских ролей для пользователей AD из базы данных: OK
  • Запомнить меня: FAIL

Моя проблема: «Запомнить меня» отлично работает, таблица persistent_logins создана успешно, она отлично хранит токены. Но когда пользователь возвращается на веб-сайт, весна показывает страницу «не авторизована».

Я думаю, что это происходит, потому что «Remember me» ничего не знает о моих собственных ролях и извлекает роли из 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

Получите ответ по вышеуказанному вопросу? Если нет, попробуйте мой ответ и перейдите по ссылке, которую я предоставил, для получения дополнительной информации о * Remember-Me Authentication *. – OO7

+0

Попробуйте с новыми ссылками в моем ответе. – OO7

+0

Я решил проблему, немного позже отправит ответ. – EatingPeopleIsFun

ответ

1

С Spring Docs Remember-Me

Если вы используете поставщика проверки подлинности, который не использует UserDetailsService (например, поставщик LDAP), то он работать не будет, если вы также иметь компонент UserDetailsService в контексте вашего приложения.

Попробуйте добавить ниже в вашем 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 Документы

голосов, если ConfigAttribute.getAttribute() из IS_AUTHENTICATED_FULLY или IS_AUTHENTICATED_REMEMBERED или IS_AUTHENTICATED_ANONYMOUSLY присутствует. Этот список находится в порядке наиболее строгой проверки минимальной строгой проверки.

Текущая проверка подлинности будет проверяться, чтобы определить, имеет ли принципал определенный уровень аутентификации. Опция «FULLY» аутентифицирована, так как пользователь полностью аутентифицирован (то есть AuthenticationTrustResolver.isAnonymous(Authentication) является ложным, а AuthenticationTrustResolver.isRememberMe(Authentication) является ложным). "REMEMBERED" предоставит доступ, если руководитель был либо аутентифицирован с помощью функции mem-me ИЛИ, либо полностью аутентифицирован. "ANONYMOUSLY" предоставит доступ, если принципал был аутентифицирован через mem-me, OR анонимно, ИЛИ через полную аутентификацию.

Все сравнения и префиксы чувствительны к регистру.

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

Теперь настроить Spring 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

EDIT 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 Remember-me». И я не знаю, как применить это к моей конфигурации. Кроме того, многие вещи отмечены как «устаревшие». Я использую Spring Security 3.2.4.RELEASE – EatingPeopleIsFun

+0

Я обновил свой ответ. Попробуйте с этим обновлением. – OO7

+0

RoleVoter заменит мои «use-expressions = true», я не хочу этого, все методы моего контроллера, аннотированные с помощью @PreAuthorize ('ExpressionHere'). И я не хочу удалять префикс «ROLE_». Моя основная проблема: я использую , после входа в систему - он назначит роли пользователям из моей базы данных. Но помните, что я ничего не знаю о CustomLdapAuthoritiesPopulator, и когда пользователь возвращается назад, он не может правильно назначать роли, которые я думаю. – EatingPeopleIsFun

0

Неправильное решение использовать

<security:ldap-user-service/> 

Если вы хотите применить пользовательские роли из базы данных для «Помни меня» функциональность.

По-прежнему необходимо реализовать пользовательский UserDetailsService и обратиться к нему из раздела mem-me.

<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

Смежные вопросы