首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Java - GoogleIdTokenVerifier验证令牌字符串返回空

Java - GoogleIdTokenVerifier验证令牌字符串返回空
EN

Stack Overflow用户
提问于 2017-03-27 10:06:38
回答 6查看 3.9K关注 0票数 5

我正在添加谷歌注册/登录到我的网络应用程序,我遇到了一个问题。

这是我的密码:

代码语言:javascript
运行
复制
private static final HttpTransport transport = new NetHttpTransport();
private static final JsonFactory jsonFactory = new JacksonFactory();
private static final String MY_APP_GOOGLE_CLIENT_ID = "wouldntyouliketoknow";

public UsernamePasswordAuthenticationToken verify(final String idTokenString){

    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
                                            .setAudience(Collections.singletonList(MY_APP_GOOGLE_CLIENT_ID))
                                            .build();

    try {
        GoogleIdToken idToken = verifier.verify(idTokenString);// <-- verifier.verify returns null !!!
        if (idToken != null) {
            Payload payload = idToken.getPayload();
            String email = payload.getEmail();
            if(Boolean.valueOf(payload.getEmailVerified())){
                UserJPA jpa = userRepository.findByEmail(email);
                if(jpa==null){
                    throw new UsernameNotFoundException("Cannot find user with email = "+email);
                }
                if(!jpa.isRegisterredWithGoogle()){
                    throw new UsernameNotFoundException("This user did not use the 'Register with google' option.");
                }
                bokiAuthenticationProvider.checkUserActiveAndUnlocked(jpa);

                return new UsernamePasswordAuthenticationToken(jpa.getUsername(), jpa.getPasswordHesh(), 
                        bokiAuthenticationProvider.getAuthorities(jpa.getUserHasRoleSecurityList()));
            }
        }else{
            System.out.println("The *idToken* object is null !!!");
        }
    } catch (GeneralSecurityException | IOException e) {
        e.printStackTrace();
    }

    throw new MyCustomException("Google token is invalid or has expired");
}

为了创建我的CLIENT_ID,我按照这里的说明:

https://developers.google.com/identity/sign-in/web/devconsole-project

问题是verifier.verify一直在返回

我查过:

  • 我的用户确实在google注册,并且数据库字段被正确地填充。
  • 每次尝试google_sign_in时,我都会从google获得不同的字符串标记。
  • 我的CLIENT_ID在google控制台中是有效的和活动的。

更令人困惑的是,就在一个月前,整件事情还很顺利。我请病假离开了,回来的时候,老板用这个问题欢迎我。

有人知道会发生什么吗?

EN

回答 6

Stack Overflow用户

发布于 2017-04-04 09:48:41

我终于想出来了。

由于没有人知道如何帮助我,所以我放弃了指定的google库,从零开始进行自己的令牌验证。

我在这里使用了google-令牌验证器-url-工具:

token=XYZ123

在这页的底部

https://developers.google.com/identity/protocols/OpenIDConnect

我找到了破译json的方法。

我所做的是,我用代码联系他们的在线工具,获取json响应并手动验证。这是我的密码:

代码语言:javascript
运行
复制
    private Map<String,String> getMapFromGoogleTokenString(final String idTokenString){
        BufferedReader in = null;
        try {
            // get information from token by contacting the google_token_verify_tool url :
            in = new BufferedReader(new InputStreamReader(
                                        ((HttpURLConnection) (new URL("https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=" + idTokenString.trim()))
                                        .openConnection()).getInputStream(), Charset.forName("UTF-8")));

            // read information into a string buffer :
            StringBuffer b = new StringBuffer();
            String inputLine;
            while ((inputLine = in.readLine()) != null){
                b.append(inputLine + "\n");
            }

            // transforming json string into Map<String,String> :
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.readValue(b.toString(), objectMapper.getTypeFactory().constructMapType(Map.class, String.class, String.class));

        // exception handling :
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch(Exception e){
            System.out.println("\n\n\tFailed to transform json to string\n");
            e.printStackTrace();
        } finally{
            if(in!=null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    // chack the "email_verified" and "email" values in token payload 
    private boolean verifyEmail(final Map<String,String> tokenPayload){
        if(tokenPayload.get("email_verified")!=null && tokenPayload.get("email")!=null){
            try{
                return Boolean.valueOf(tokenPayload.get("email_verified")) && tokenPayload.get("email").contains("@gmail.");
            }catch(Exception e){
                System.out.println("\n\n\tCheck emailVerified failed - cannot parse "+tokenPayload.get("email_verified")+" to boolean\n");
            }
        }else{
            System.out.println("\n\n\tCheck emailVerified failed - required information missing in the token");
        }
        return false;
    }

    // check token expiration is after now :
    private boolean checkExpirationTime(final Map<String,String> tokenPayload){
        try{
            if(tokenPayload.get("exp")!=null){
                // the "exp" value is in seconds and Date().getTime is in mili seconds
                return Long.parseLong(tokenPayload.get("exp")+"000") > new java.util.Date().getTime();
            }else{
                System.out.println("\n\n\tCheck expiration failed - required information missing in the token\n");
            }
        }catch(Exception e){
            System.out.println("\n\n\tCheck expiration failed - cannot parse "+tokenPayload.get("exp")+" into long\n");
        }
        return false;
    }

    // check that at least one CLIENT_ID matches with token values
    private boolean checkAudience(final Map<String,String> tokenPayload){
        if(tokenPayload.get("aud")!=null && tokenPayload.get("azp")!=null){
            List<String> pom = Arrays.asList("MY_CLIENT_ID_1",
                                             "MY_CLIENT_ID_2",
                                             "MY_CLIENT_ID_3");

            if(pom.contains(tokenPayload.get("aud")) || pom.contains(tokenPayload.get("azp"))){
                return true;
            }else{
                System.out.println("\n\n\tCheck audience failed - audiences differ\n");
                return false;
            }
        }
        System.out.println("\n\n\tCheck audience failed - required information missing in the token\n");
        return false;
    }

    // verify google token payload :
    private boolean doTokenVerification(final Map<String,String> tokenPayload){
        if(tokenPayload!=null){
            return verifyEmail(tokenPayload) // check that email address is verifies 
                && checkExpirationTime(tokenPayload) // check that token is not expired
                && checkAudience(tokenPayload) // check audience
                ; 
        }
        return false;
    }

一旦我完成了这个详细的验证,我就能够准确地看到错误所在;前端发送给我的是无效的CLIENT_ID值。抱怨,抱怨,我已经问了他们大约100次了,他们告诉我,这些价值观是一致的。BAH !

因此,这个错误不是在我的原始代码中进行令牌验证的地方,而是我办公室通信中的一个错误。

为了安全起见,我将回到原来的代码,使用适当的CLIENT_ID。不过,我必须对谷歌图书馆提出一个抱怨--它从未告诉我为什么令牌验证失败。我花了好几天的努力才终于弄清楚了。我知道他们是出于安全考虑才这么做的,但缺乏支持仍然令人难堪。

票数 6
EN

Stack Overflow用户

发布于 2018-06-28 02:59:36

也有类似的问题。太蠢了,但我在服务器上的时间不对。我认为验证总是失败的,因为令牌的过期时间结束在我的服务器时间范围内。

票数 5
EN

Stack Overflow用户

发布于 2021-03-03 12:57:38

对于仍有此问题的人:

  1. 尝试python示例(https://developers.google.com/identity/sign-in/web/backend-auth#python)以确保您使用的是正确的CLIENT_ID,如果不是,您将看到带有右CLIENT_ID的异常消息。
  2. 使用版本com.google.api-client of 1.31.3com.google.oauth-client of version 1.31.4
  3. 使用com.google.api.client.json.gson.GsonFactory作为JsonFactory
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43043526

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档