【问题标题】:How do I change a user's password using Google's Directory API using Java?如何使用 Java 使用 Google 的 Directory API 更改用户密码?
【发布时间】:2014-10-21 05:12:26
【问题描述】:

我要提前道歉,我可能遗漏了一些信息,让任何人都知道我的设置是正确的。请让我知道我还需要说明什么。 我在一所大学工作,我们通过 Google 托管学生电子邮件帐户。他们经常忘记密码并且必须重置密码。如果他们验证了有关自己的足够信息,我们有一个页面可以设置他们的密码。我们一直在使用的代码已被 Google 弃用,取而代之的是他们的 Directory API。我的任务是转换这个旧代码:

//changes a password
@Override
public void reset ( final String username, final String password ) throws ResetException
{
    try
    {
        Validate.notEmpty( username );
        Validate.notEmpty( password );

        final AppsForYourDomainClient client = ClientFactory.getClient();  //admin-user account
        final UserEntry entry = client.retrieveUser( username );
        Validate.isTrue( !entry.getLogin().getSuspended(), "Account is suspended." );
        entry.getLogin().setPassword( password );
        client.updateUser( username, entry );
    }
    catch ( final Exception e )
    {
        throw new ResetException( e );
    }
}

使用他们的新 API。我已经阅读了很多文档和几个示例,但似乎都没有帮助。我已经通过他们的 Admin Console 为我们的管理员帐户启用了 Admin SDK,并注册了我的应用程序并从他们的开发者控制台获得了一个密钥,但我似乎无法收到任何返回我想要的东西的请求。现在我只是想获取用户列表:

public void testList () throws Exception
{
    InputStream is = null;
    final String accessToken = getAccessToken();
    final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    final JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
    final File p12 = new File( GoogleResetterTest.class.getClassLoader().getResource("ead05e56893a.p12").toURI() );
    GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
                                        .setJsonFactory(jsonFactory)
                                        .setServiceAccountId( SERVICE_ACCOUNT_EMAIL )
                                        .setServiceAccountScopes( PlusScopes.all() )
                                        .setServiceAccountPrivateKeyFromP12File( p12 )  //password: notasecret
                                        .setClientSecrets(CLIENT_ID, CLIENT_SECRET)
                                        .build();
    final Directory dir = new Directory.Builder( httpTransport, jsonFactory, credential)
                                                .setApplicationName( "API Project" )
                                                .build();
    final Directory.Users diruser = dir.users();
    final Directory.Users.List diruserlist = diruser.list().setDomain( EMAIL_DOMAIN );
    final HttpResponse response = diruserlist.executeUsingHead();
    is = response.getContent();
    StringWriter writer = new StringWriter();
    IOUtils.copy(is, writer, "UTF-8");
    String theString = writer.toString();
    IOUtils.closeQuietly( is );
}

在 diruserlist.executeUsingHead();我得到这个回复:

com.google.api.client.auth.oauth2.TokenResponseException: 400 错误请求 { “错误”:“invalid_grant” }

对我来说,这是一条非常无用的错误消息,因为似乎有 4 或 5 条错误消息可能会导致该响应。

我不禁会想,我把这件事弄得太复杂了。我喜欢原始代码的简单性,而对新 API 的一些回应批评它更复杂。有没有人必须这样做并且可以指出我解决此问题的正确路径?

【问题讨论】:

    标签: java google-directory-api google-oauth-java-client


    【解决方案1】:

    我将回答我自己的问题并将其标记为已解决,而不是删除或编辑问题。

    这里有一些事情在起作用,有些事情我弄错了。主要是因为构建器的文档很差或不存在。这是我必须做的:

    在管理控制台上设置 (https://admin.google.com/)

    1. 确保我尝试使用的帐户对此域具有超级管理员访问权限(管理员角色 -> 超级管理员)
    2. 在“安全”选项卡中启用 API 访问。 (安全 -> API 参考 -> API 访问 -> 启用 API 访问
    3. 在开发者控制台上进行设置(如下)
    4. 添加对我的应用程序的访问权限(安全 -> 高级设置 -> 身份验证 -> 管理 OAuth 客户端访问权限
    5. 从开发者控制台将这些范围授予我的客户(不带方括号):
      • [https]://www.googleapis.com/auth/admin.directory.group
      • [https]://www.googleapis.com/auth/admin.directory.user
      • [https]://www.googleapis.com/auth/admin.directory.user.readonly
      • [https]://www.googleapis.com/auth/admin.directory.user.security

    并非所有这些范围都是必需的,但这是我启用的

    在开发者控制台上设置 (https://console.developers.google.com/)

    1. 创建项目
    2. 为项目启用 Admin SDK(API 和身份验证 -> API)
    3. 将 OAuth 客户端创建为服务帐户(API 和身份验证 -> 凭据 -> 创建新的客户端 ID)
    4. 将该客户端 ID 带回管理控制台(上面的第 5 步)
    5. 在我的 java 应用程序可以读取的地方下载 p12 密钥。 (API 和身份验证 -> 凭据 -> 生成新的 P12 密钥)

    现在我终于可以写一些java了!

    import java.io.*;
    import java.net.*;
    import java.util.*;
    
    import java.security.GeneralSecurityException;
    import java.security.MessageDigest;
    
    import javax.xml.bind.DatatypeConverter;
    
    import com.google.api.services.admin.directory.*;
    import com.google.api.services.admin.directory.model.*;
    import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
    import com.google.api.client.http.javanet.NetHttpTransport;
    import com.google.api.client.json.jackson2.JacksonFactory;
    
    import org.apache.commons.lang.StringUtils;
    import org.apache.commons.lang.Validate;
    
    public class PasswordResetter
    {
        public void changePassword( final String username, final String newPassword ) throws Exception
        {
            final Directory dir = getDirectory();
            final User studentUser = updatePassword( getUser( dir, DOMAIN_NAME, username ), password );
            updateDirectory( dir, studentUser );
        }
    
        private Directory getDirectory() throws IOException, GeneralSecurityException, URISyntaxException
        {
            final NetHttpTransport httpTransport = new NetHttpTransport();
            final JacksonFactory jsonFactory = new JacksonFactory();
            final File p12 = new File( P12_FILENAME );
            final GoogleCredential credential = new GoogleCredential.Builder()
                                                                        .setTransport(httpTransport)
                                                                        .setJsonFactory(jsonFactory)
                                                                        .setServiceAccountUser( SUPER_USER_EMAIL )
                                                                        .setServiceAccountId( SERVICE_ACCOUNT_EMAIL_FROM_DEV_CONSOLE ) //the one that ends in "@developer.gserviceaccount.com"
                                                                        .setServiceAccountScopes( getCredentials() )
                                                                        .setServiceAccountPrivateKeyFromP12File( p12 )
                                                                        .build();
            return new Directory.Builder( httpTransport, jsonFactory, null)
                                        .setHttpRequestInitializer( credential )
                                        .setApplicationName( "API Project" )    //Not necessary, but silences a runtime warning using any not-blank string here
                                        .build();
        }
    
        private List<String> getCredentials()
        {
            final List<String> toReturn = new LinkedList<String>();
            toReturn.add( DirectoryScopes.ADMIN_DIRECTORY_GROUP );
            toReturn.add( DirectoryScopes.ADMIN_DIRECTORY_USER );
            toReturn.add( DirectoryScopes.ADMIN_DIRECTORY_USER_READONLY );
            toReturn.add( DirectoryScopes.ADMIN_DIRECTORY_USER_SECURITY );
            return toReturn;
        }
    
        private Users getUser( final Directory dir, final String domain, final String username ) throws Exception
        {
            Directory.Users.List diruserlist = dir.users().list()
                                                            .setDomain( domain )
                                                            .setQuery( "email:" + username + "*" );
            return diruserlist.execute();
        }
    
        private User updatePassword( final User user, final String password ) throws Exception
        {
            final MessageDigest md = MessageDigest.getInstance( "MD5" );    //I've been warned that this is not thread-safe
            final byte[] digested = md.digest( password.getBytes( "UTF-8" ) );
            final String newHashword = DatatypeConverter.printHexBinary( digested );
            return user.setHashFunction("MD5")                              //only accepts MD5, SHA-1, or CRYPT
                        .setPassword( newHashword );
        }
    
        private void updateDirectory( final Directory dir, final User user ) throws IOException
        {
            final Directory.Users.Update updateRequest = dir.users().update( user.getPrimaryEmail(), user );
            updateRequest.execute();
        }
    }
    

    现在我有一个很好的方法让我的超级用户帐户只提供一个用户名(它定义了他们的电子邮件,因此定义了查询)和一个新密码,它会为他们更改它。请注意,这不是完整的类,不会仅通过复制粘贴进行编译。

    我当然希望我没有跳过一步。我的控制台中有一些额外的设置,我害怕摆脱它,以防它破坏某些东西。希望这会在未来对其他人有所帮助。

    【讨论】:

    • 我遵循了您的所有步骤和其他必需的逻辑,但我坚持使用 updatePassword 方法,因为它调用 getUser,它返回用户,而 studentUser 是用户类型,您如何将用户转换为用户。请稍微解释一下。
    • 我也想知道这个 - 用户!=用户
    • 这个答案与developers.google.com/identity/protocols/OAuth2ServiceAccount结合使用很有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多