14

すべてのユーザーがログイン中に自分のユーザー名を変更できるようにする必要があります。問題はPrincipal、Spring Security の認証トークンでユーザー名 ( ) を更新する方法です。

(一部のビジネス ユース ケースでは、認証トークンのプリンシパル名を使用してユーザーを識別するため、更新する必要があります。 )

私はフォームベースと Cookie Rememeber Me ベースのログインを使用しているため、認証トークンはUsernamePaswordAuthenticationTokenRememberMeAuthenticationTokenです。どちらにもprincipal、ログイン名が格納されるフィールドがあります。残念ながら、この変数はfinalであるため、その値を変更することはできません。

Spring SecurityPrincipalが認証トークンの変更を推奨する方法を知っている人はいますか?

私の現在の回避策は、 and を最終ではない追加のプリンシパル フィールドを持つサブクラスに置き換え、UsernamePaswordAuthenticationTokenメソッドRememberMeAuthenticationTokenをオーバーライドしてgetPrincipal()、元のプリンシパルの代わりにこの追加のプリンシパルを返すことです。次に、このトークンを生成する 2 つのクラスをサブクラス化して、元のトークンの代わりにトークンを作成しました。---しかし、これは大きなハックだと思います。

4

3 に答える 3

10

私は似たようなことをしましたが、それは少しハックですが、私がしたことは、新しい UserDetails を変更して保存し、更新された資格情報のセッションに新しい認証トークンを追加することでした:

Authentication request = new UsernamePasswordAuthenticationToken(user.getUsername(), password);
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
于 2012-12-24T09:39:05.987 に答える
9

なぜトークン、つまりAuthenticationサブクラスを使用するのですか? Authentication.getPrincipal()あなたの場合、 のインスタンスを返しませんUserDetailsか?

認証中に独自のUserDetails実装(setUsername()メソッドを含むもの)を提供した場合、私があなたのケースを正しく理解していれば、あなたは家から解放されます。

于 2012-12-23T21:28:49.270 に答える
5

Marcel Stö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 <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);
    }
}

こちらもどなたか使っていただければ幸いです。

于 2013-01-05T17:28:09.210 に答える