如何使用Spring Security 3.1更改当前用户的登录名?
我要求每个用户在登录时都可以更改自己的用户名。问题是如何在Spring Security的身份validation令牌中更新用户名( Principal
)?
( 我必须更新它,因为我使用身份validation令牌中的原始名称来识别某些业务用例中的用户。 )
我使用基于表单和cookie记忆我的登录,所以我的身份validation令牌是UsernamePaswordAuthenticationToken
和RememberMeAuthenticationToken
。 两者都有一个存储登录名的字段principal
。 不幸的是这个变量是final
,所以我不能改变它的价值。
有没有人知道Spring Security如何推荐更改身份validation令牌中的Principal
?
我当前的工作原理是我将UsernamePaswordAuthenticationToken
和RememberMeAuthenticationToken
替换为具有附加非最终主要字段的子类,并覆盖getPrincipal()
方法以返回此附加主体而不是原始主体。 然后我还将生成此标记的两个类子类化,以创建我的标记而不是原始标记。 —但我觉得这是一个很大的黑客。
为什么选择令牌即Authentication
子类? Authentication.getPrincipal()
是否在您的情况下返回UserDetails
的实例?
如果你提供了自己的UserDetails
实现(一个带有setUsername()
方法),那么如果我正确理解你的情况,那么在你认证你是免费的。
我做了类似的事情,这有点像黑客,但我所做的是更改并保存新的UserDetails,然后为会话添加一个新的身份validation令牌以获取更新的凭据:
Authentication request = new UsernamePasswordAuthenticationToken(user.getUsername(), password); Authentication result = authenticationManager.authenticate(request); SecurityContextHolder.getContext().setAuthentication(result);
我实施了MarcelStör提出的想法。
为什么选择令牌即认证子类? 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
User
with the details required by * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider}. * * @param username the username presented to the *DaoAuthenticationProvider
* @param password the password that should be presented to the *DaoAuthenticationProvider
* @param enabled set totrue
if the user is enabled * @param accountNonExpired set totrue
if the account has not * expired * @param credentialsNonExpired set totrue
if the credentials * have not expired * @param accountNonLocked set totrue
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 anull
value was passed * either as a parameter or as an element in the *GrantedAuthority
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 *DaoAuthenticationProvider
* @param password the password that should be presented to the *DaoAuthenticationProvider
* @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. ** 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 loadUserByUsername 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 combinedAuthorities) { String returnUsername = userFromUserQuery.getUsername(); if (!isUsernameBasedPrimaryKey()) { returnUsername = username; } return new UpdateableUserDetails(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(), true, true, true, combinedAuthorities); } }
我希望有人也可以使用它。