Как изменить имя пользователя для текущего пользователя с помощью Spring Security 3.1?
У меня есть требование, чтобы каждый пользователь мог изменить свое имя пользователя во время входа в систему. Проблема заключается в том, как обновить имя пользователя (Principal
) в Spring токене аутентификации безопасности?
(Я должен обновить его, потому что я использую имя prinicpal из токена аутентификации, чтобы идентифицировать пользователя в некоторых случаях использования в бизнесе.)
Я использую основанную на форме и cookie память для входа в систему, поэтому мои токены аутентификации UsernamePaswordAuthenticationToken
и RememberMeAuthenticationToken
. Оба имеют поле Principal
, где хранится имя входа. К сожалению, эта переменная final
, поэтому я не могу изменить ее значение.
Кто-нибудь имеет представление о том, как Spring Security рекомендует изменить Principal
в токене аутентификации?
Мой текущий рабочий стол заключается в том, что я заменил подклассы UsernamePaswordAuthenticationToken
и RememberMeAuthenticationToken
подклассами, у которых есть дополнительное не окончательное основное поле и переопределить метод getPrincipal()
, чтобы вернуть этот дополнительный главный, а не оригинальный. Затем я также подклассифицировал два класса, которые генерируют эти токены для создания моих токенов вместо исходного. --- Но я чувствую, что это большой взлом.
Ответы
Ответ 1
Зачем идти с токеном i.e. Authentication
подклассы? Не возвращает Authentication.getPrincipal()
экземпляр UserDetails
в вашем случае?
Если вы предоставили свою собственную реализацию UserDetails
(один с методом setUsername()
), а при аутентификации вы свободны дома, если я правильно понимаю ваш случай.
Ответ 2
Я сделал что-то подобное, и это немного взломало, но то, что я сделал, это изменение и сохранение новых UserDetails, а затем добавление нового нового токена аутентификации в сеанс для обновленных учетных данных:
Authentication request = new UsernamePasswordAuthenticationToken(user.getUsername(), password);
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
Ответ 3
Я реализовал идею, предложенную Марселем Штер.
Зачем идти с токеном, то есть подклассы аутентификации? Не проверяет подлинность Authentication.getPrincipal() экземпляр UserDetails в вашем случае?
Если вы предоставили собственную реализацию UserDetails (один с помощью метода setUsername()), при аутентификации вы свободны дома, если я правильно понимаю ваш случай.
И я хочу поделиться им:
Это объект UserDetails с изменяемым именем пользователя. Я сделал его подклассом org.springframework.security.core.userdetails.User
, потому что я использую его вместе с Jdbc User Details Service, которые нормально создают эти классы.
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();
}
}
Подкласс для 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);
}
}
Я надеюсь, что кто-то тоже сможет его использовать.