2012-12-23 11 views
11

Mam wymóg, aby każdy użytkownik mógł zmienić swoją nazwę użytkownika, gdy jest on zalogowany. Problem polega na tym, jak zaktualizować nazwę użytkownika (Principal) w torze uwierzytelniania Spring Security?Jak zmienić nazwę logowania dla bieżącego użytkownika za pomocą Spring Security 3.1?

(muszę go zaktualizować, bo używają nazwy prinicpal z tokenu uwierzytelniania do identyfikacji użytkownika w niektórych przypadkach zastosowań biznesowych.)

używam forma oparta i ciasteczka rememeber mi logowanie oparciu więc moja Tokeny uwierzytelniające to UsernamePaswordAuthenticationToken i RememberMeAuthenticationToken. Oba mają pole principal, w którym przechowywana jest nazwa logowania. Niestety ta zmienna to final, więc nie mogę zmienić jej wartości.

Czy ktoś ma pomysł, jak Spring Security zaleca zmianę Principal w torze uwierzytelnienia?

Mój obecny workarround jest to, że zastąpił UsernamePaswordAuthenticationToken i RememberMeAuthenticationToken z podklasy, które mają dodatkowy nie ostateczną głównym polu i zastąpić metodę getPrincipal() powrót ten dodatkowy kapitał zamiast oryginalnego. Następnie podzieliłem też dwie klasy, które generują to tokeny, aby utworzyć moje tokeny zamiast oryginalnych. --- Ale wydaje mi się, że to wielki hack.

Odpowiedz

6

Dlaczego warto używać podklas token tj. Authentication? Czy w twoim przypadku Authentication.getPrincipal() nie zwraca wystąpienia instancji UserDetails?

Jeśli dostarczyłeś własną implementację UserDetails (jedną z metodą setUsername()) podczas uwierzytelniania, jesteś w domu, jeśli dobrze rozumiem twoją sprawę.

+0

Zaimplementowałem twój pomysł i zadziałało. http://stackoverflow.com/a/14174404/280244 – Ralph

7

robiłem coś podobnego, i jest to bit hack, ale to, co zrobiłem było zmienić i zapisać nowe UserDetails, a następnie dodać nowy nowy uwierzytelniania token do sesji dla zaktualizowanych mandatów:

Authentication request = new UsernamePasswordAuthenticationToken(user.getUsername(), password); 
Authentication result = authenticationManager.authenticate(request); 
SecurityContextHolder.getContext().setAuthentication(result); 
+0

działa na moim przypadku przez @Autowired AuthenticationManager, aby uzyskać uwierzytelnianieManager – Dickson

4

Zaimplementowałem pomysł zaproponowany przez Marcela Stör.

Dlaczego warto korzystać z tokenu, np. Podklasy uwierzytelniania? Czy funkcja Authentication.getPrincipal() nie zwraca instancji UserDetails w twoim przypadku?

Jeśli dostarczyłeś własną implementację UserDetails (jedną z użyciem metody setUsername()) podczas uwierzytelniania, że ​​jesteś w domu, jeśli dobrze rozumiem twoją sprawę.

I chcę dzielić realizacji:

To jest UserDetails obiekt z modyfikowalny użytkownika. Zrobiłem to podklasę org.springframework.security.core.userdetails.User, ponieważ używam go razem z Jdbc User Details Service, która normalnie tworzy te klasy.

import org.springframework.security.core.GrantedAuthority; 
import org.springframework.security.core.userdetails.User; 
/** 
* Extension of {@link User} where it is possible to change the username. 
*/ 
public class UpdateableUserDetails extends User { 

    /** The Constant serialVersionUID. */ 
    private static final long serialVersionUID = 9034840503529809003L; 

    /** 
    * The user name that can be modified. 
    * It "overrides" the username field from class {@link User}. 
    */ 
    private String modfiableUsername; 

    /** 
    * Construct the <code>User</code> with the details required by 
    * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider}. 
    * 
    * @param username the username presented to the 
    *  <code>DaoAuthenticationProvider</code> 
    * @param password the password that should be presented to the 
    *  <code>DaoAuthenticationProvider</code> 
    * @param enabled set to <code>true</code> if the user is enabled 
    * @param accountNonExpired set to <code>true</code> if the account has not 
    *  expired 
    * @param credentialsNonExpired set to <code>true</code> if the credentials 
    *  have not expired 
    * @param accountNonLocked set to <code>true</code> if the account is not 
    *  locked 
    * @param authorities the authorities that should be granted to the caller 
    *  if they presented the correct username and password and the user 
    *  is enabled. Not null. 
    * 
    * @throws IllegalArgumentException if a <code>null</code> value was passed 
    *   either as a parameter or as an element in the 
    *   <code>GrantedAuthority</code> collection 
    */ 
    public UpdateableUserDetails(final String username, final String password, final boolean enabled, 
      final boolean accountNonExpired, final boolean credentialsNonExpired, final boolean accountNonLocked, 
      final Collection<? extends GrantedAuthority> authorities) throws IllegalArgumentException { 
     super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); 
     this.modfiableUsername = username; 
    } 

    /** 
    * Calls the more complex constructor with all boolean arguments set to {@code true}. 
    * @param username the username presented to the 
    *  <code>DaoAuthenticationProvider</code> 
    * @param password the password that should be presented to the 
    *  <code>DaoAuthenticationProvider</code> 
     * @param authorities the authorities that should be granted to the caller 
    *  if they presented the correct username and password and the user 
    *  is enabled. Not null. 
    */ 
    public UpdateableUserDetails(final String username, final String password, 
      final Collection<? extends GrantedAuthority> authorities) { 
     super(username, password, authorities); 
     this.modfiableUsername = username; 
    } 

    /** 
    * Return the modifiable username instead of the fixed one. 
    * 
    * @return the username 
    */ 
    @Override 
    public String getUsername() { 
     return this.modfiableUsername; 
    } 

    public void setUsername(final String username) { 
     this.modfiableUsername = username; 
    } 

    /** 
    * Returns {@code true} if the supplied object is a {@code User} instance with the 
    * same {@code username} value. 
    * <p> 
    * In other words, the objects are equal if they have the same user name, representing the 
    * same principal. 
    * 
    * @param rhs the other object 
    * @return true if equals 
    */ 
    @Override 
    public boolean equals(final Object rhs) { 
     if (rhs instanceof User) { 
      return this.modfiableUsername.equals(((User) rhs).getUsername()); 
     } 
     return false; 
    } 

    /** 
    * Returns the hashcode. 
    * 
    * In order not to get any problems with any hash sets that based on the fact that this hash is not changed 
    * over livetime and not to fail one of the constraints for {@link Object#hashCode()}, 
    * this method always returns the same constant hash value. 
    * 
    * I expect that this is no such deal, because we expect not to have so many logged in users, so the hash sets 
    * that use this as an key will not get so slow. 
    * 
    * @return the hash 
    */ 
    @Override 
    public int hashCode() { 
     return 1; 
    } 

    /** 
    * Like {@link User#toString()}, but print the modifiable user name. 
    * 
    * @return the string representation with all details 
    */ 
    @Override 
    public String toString() { 
     StringBuilder sb = new StringBuilder(); 
     sb.append(super.toString()).append(": "); 
     sb.append("Username: ").append(this.modfiableUsername).append("; "); 
     sb.append("Password: [PROTECTED]; "); 
     sb.append("Enabled: ").append(isEnabled()).append("; "); 
     sb.append("AccountNonExpired: ").append(isAccountNonExpired()).append("; "); 
     sb.append("credentialsNonExpired: ").append(isCredentialsNonExpired()).append("; "); 
     sb.append("AccountNonLocked: ").append(isAccountNonLocked()).append("; "); 

     if (!getAuthorities().isEmpty()) { 
      sb.append("Granted Authorities: "); 

      boolean first = true; 
      for (GrantedAuthority auth : getAuthorities()) { 
       if (!first) { 
        sb.append(","); 
       } 
       first = false; 

       sb.append(auth); 
      } 
     } else { 
      sb.append("Not granted any authorities"); 
     } 
     return sb.toString(); 
    }  
} 

podklasy dla UserDetailsService

import org.springframework.security.core.GrantedAuthority; 
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl; 
/** 
* Create {@link UpdateableUserDetails} instead of {@link org.springframework.security.core.userdetails.User} user details. 
*/ 
public class JdbcDaoForUpdatableUsernames extends JdbcDaoImpl { 

    /** 
    * Instantiates a new jdbc dao for updatable usernames impl. 
    * 
    * @param privilegesService the privileges service 
    */ 
    public JdbcDaoForUpdatableUsernames(final PrivilegesService privilegesService) { 
     super(privilegesService); 
    } 

    /** 
    * Can be overridden to customize the creation of the final UserDetailsObject which is 
    * returned by the <tt>loadUserByUsername</tt> method. 
    * 
    * @param username the name originally passed to loadUserByUsername 
    * @param userFromUserQuery the object returned from the execution of the 
    * @param combinedAuthorities the combined array of authorities from all the authority loading queries. 
    * @return the final UserDetails which should be used in the system. 
    */ 
    @Override 
    protected UserDetails createUserDetails(final String username, final UserDetails userFromUserQuery, 
      final List<GrantedAuthority> combinedAuthorities) { 
     String returnUsername = userFromUserQuery.getUsername(); 

     if (!isUsernameBasedPrimaryKey()) { 
      returnUsername = username; 
     } 

     return new UpdateableUserDetails(returnUsername, 
       userFromUserQuery.getPassword(), 
       userFromUserQuery.isEnabled(), 
       true, 
       true, 
       true, 
       combinedAuthorities); 
    } 
} 

Mam nadzieję, że ktoś może go używać zbyt.

Powiązane problemy