java 如何使用 Spring Security 3.1 更改当前用户的登录名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14010326/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to change the login name for the current user with Spring Security 3.1?
提问by Ralph
I have the requirement that every user can change his own user name while he stays logged in. The problem is how to update the username (Principal
) in Spring Security`s Authentication Token?
我要求每个用户在保持登录状态时都可以更改自己的用户名。问题是如何更新Principal
Spring Security 的 Authentication Token 中的用户名 ( )?
(I have to update it, because I use the prinicpal name from the Authentication Token to identify the user in some business use cases.)
(我必须更新它,因为我在某些业务用例中使用身份验证令牌中的主体名称来识别用户。)
I use form based and cookie rememeber me based login so my Authentication Tokens are UsernamePaswordAuthenticationToken
and RememberMeAuthenticationToken
. Both have a field principal
where the login name is stored. Unfortunately this variable is final
, so I can not change its value.
我使用基于表单和 cookie 记住我的登录,所以我的身份验证令牌是UsernamePaswordAuthenticationToken
和RememberMeAuthenticationToken
。两者都有一个principal
存储登录名的字段。不幸的是final
,这个变量是,所以我不能改变它的值。
Does anybody has an idea how Spring Security recomends to change the Principal
in the Authentication Token?
有没有人知道 Spring Security 建议如何更改Principal
身份验证令牌中的 ?
My current workarround is that I replaced the UsernamePaswordAuthenticationToken
and RememberMeAuthenticationToken
with subclasses that have an additional not final principal field and override the getPrincipal()
method to return this additional principal instead of the original one. Then I have also subclassed the two classes that generate this tokens to create my tokens instead of the original one. --- But I feel that this is a big hack.
我目前的解决方法是我用具有额外的非最终主体字段的子类替换UsernamePaswordAuthenticationToken
和RememberMeAuthenticationToken
,并覆盖getPrincipal()
方法以返回这个额外的主体而不是原始主体。然后,我还对生成此令牌的两个类进行了子类化,以创建我的令牌而不是原始令牌。--- 但我觉得这是一个很大的黑客。
采纳答案by Marcel St?r
Why go with tokeni.e. Authentication
subclasses? Doesn't Authentication.getPrincipal()
return an instance of UserDetails
in your case?
为什么要使用令牌即Authentication
子类?在您的情况下不Authentication.getPrincipal()
返回实例UserDetails
吗?
If you supplied your own UserDetails
implementation (one with a setUsername()
method) while authenticating you're home free if I understand your case correctly.
如果您在验证时提供了自己的UserDetails
实现(一个带有setUsername()
方法的实现),如果我正确理解您的情况,您就可以免费回家。
回答by John Farrelly
I've done something similar, and it's a bit of a hack but what I did was change and save the new UserDetails, and then add a new a new authentication token to the session for the updated credentials:
我做了类似的事情,这有点像黑客,但我所做的是更改并保存新的 UserDetails,然后为更新的凭据向会话添加一个新的新身份验证令牌:
Authentication request = new UsernamePasswordAuthenticationToken(user.getUsername(), password);
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
回答by Ralph
I have implemented the idea proposed by Marcel St?r.
我已经实现了 Marcel St?r 提出的想法。
Why go with token i.e. Authentication subclasses? Doesn't Authentication.getPrincipal() return an instance of UserDetails in your case?
If you supplied your own UserDetails implementation (one with a setUsername() method) while authenticating you're home free if I understand your case correctly.
为什么要使用令牌,即身份验证子类?在您的情况下,Authentication.getPrincipal() 不返回 UserDetails 的实例吗?
如果您在验证时提供了自己的 UserDetails 实现(一个带有 setUsername() 方法),如果我正确理解您的情况,您就可以免费回家。
And I want to share the implementation:
我想分享实现:
This is the UserDetails object with the modifiable username. I made it a subclass of org.springframework.security.core.userdetails.User
because I use it together with Jdbc User Details Service that normaly create this classes.
这是具有可修改用户名的 UserDetails 对象。我将它作为一个子类,org.springframework.security.core.userdetails.User
因为我将它与通常创建此类的 Jdbc 用户详细信息服务一起使用。
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();
}
}
The Subclass for the UserDetailsService
的子类 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);
}
}
I hope someone can use it too.
我希望有人也可以使用它。