Java实现Google第三方登录
?????? 其實所有的第三方登錄都是基于OAuth協議的,大多數平臺都支持OAuth2.0,只有Twitter的是基于OAuth1.0來做的。所以只要弄明白一個,其他的什么qq、微信、微博的第三方登錄也都一樣。上一篇寫的Facebook,現在再寫一個Google,兩篇都看完的同學就會明白“道理都是相通的”這句話的意思了!
?????? 我做第三方登錄的目的僅僅是獲取到用戶信息,然后將用戶信息和本地程序的某一部分綁定,保存到數據庫,如果有同學要獲取其他的信息,那就獲取到accessToken后去找API即可。
下面依然是demo,只提供思路,代碼寫的比較簡單,都寫到一個類中,方便同學理解。
開發流程:
1、了解OAuth2.0
2、到Google官網注冊開發者賬號,創建憑據(就是應用)
3、代碼實現
下面逐步介紹
1、了解OAuth2.0(非常重要)
因為第三方登錄離不開OAuth協議,了解這個是必要的。
【摘自Google官網】
注:有英語基礎的同學強烈建議看原文,千萬不要用Google翻譯,翻譯完了全是坑,更糊涂!(我的英語不太好,只限于理解但是翻譯不出來,所以具體的流程看下面的流程圖就可以了,或者看上一篇的facebook的OAuth介紹)
算了,我還是丟回人吧,大概翻譯一下流程(純人工翻譯,可能不準確,但是不會影響你的理解):
Google的OAuth2.0支持多種語言PHP, Java, Python, Ruby,? ASP.NET.
這個授權從你的應用重定向到Google的url開始,你請求url時攜帶的參數要標明你想獲取的參數類型。Google來處理用戶的身份驗證、session、還有用戶是否授權,當用戶授權成功后Google會給你返回一個授權的code(通過在應用里定義好的回調地址傳回code參數),然后你就可以用這code(臨時令牌)調Google API來換取access_Token(授權令牌)和refresh_Token(刷新令牌)。
你的應用需要存儲這個refresh_Token(刷新令牌)留著以后你的access_Token(授權令牌)過期的時候使用,然后就可以用access_Token(授權令牌)來訪問Google API 獲取用戶信息了。
(我去:翻譯完感覺自己不會說人話了,英語不好的同學湊合著看吧,重要的理解下面的流程圖)
2、到Google官網注冊開發者賬號,創建憑據(就是應用)
②創建應用https://console.developers.google.com/apis/credentials
<1>創建憑據
創建完成后會看到客戶端id和客戶端秘鑰(妥善保存,代碼中要用)
如果那個重定向的地址沒看懂的話往后看代碼就理解了,然后定義同意屏幕,是用戶授權登錄時看到的頁面
到此,應用就創建完成了
3、代碼實現(分為兩類同學,分別找對應自己的實現方式)
【靠自己】想自主實現的同學,我截圖具體位置,慢慢摸索同樣可以做出來
登錄開發者平臺 https://developers.google.com/??? 然后跟著截圖一步一步往下走
https://developers.google.com/identity/choose-auth
先照著官網寫個頁面體驗一下,直接復制粘貼就可以https://developers.google.com/identity/sign-in/web/
體驗完之后到guides頁面,將基本的四部參照文檔完成https://developers.google.com/identity/sign-in/web/devconsole-project
上面的四步是web頁面,下面做后臺驗證https://developers.google.com/identity/choose-auth
上面的5步做完之后來這找獲取用戶信息的API(我做的時候這個接口找的很費勁)
獲取用戶信息選這個
想查詢請求url點第二個紅框,requestUrl會自動填充(這一步就是想找到獲取用戶信息的Url,獲取到url就可以在后臺調用接口了)
到此,獲取到用戶信息之后就可以和自己的程序進項綁定了,后面的登錄邏輯視業務要求而定。
【無力者】不想自己找文檔,不想思考,著急做的同學,看我上傳的代碼,復制粘貼就可以了
①google.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang="en"> <head> <meta name="google-signin-scope" content="profile email"> <meta name="google-signin-client_id"content='客戶端ID'> <script src="https://apis.google.com/js/platform.js" async defer></script> </head> <body><div class="g-signin2" data-onsuccess="onSignIn"></div><a href="#" οnclick="signOut();">Sign out</a><script>function signOut() {var auth2 = gapi.auth2.getAuthInstance();auth2.signOut().then(function() {console.log('User signed out.');});}function onSignIn(googleUser) {//跳轉到http://gntina.iok.la/sendRedirect(獲取用戶信息)location.href = "http://gntina.iok.la/sendRedirect";//獲取用戶基本信息,但是此id不能給后臺用,不安全,改用id_token/*從這往下的代碼都不需要,因為是在后臺驗證,后臺獲取用戶信息var profile = googleUser.getBasicProfile();console.log('google自己封裝好的獲取用戶信息');console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.console.log('Name: ' + profile.getName());console.log('Image URL: ' + profile.getImageUrl());console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.//將id_token發送給后臺進行驗證var id_token = googleUser.getAuthResponse().id_token;var xhr = new XMLHttpRequest();xhr.open('POST', 'http://gntina.iok.la/idToken');xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xhr.onload = function() {console.log('Signed in as: ' + xhr.responseText);};xhr.send('idtoken=' + id_token);*/}</script> </body> </html>②controller package com.lenovo.login.controller;import java.util.HashMap; import java.util.Map; import java.util.UUID;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import net.sf.json.JSONObject;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;/*** @ClassName: LoginController* @Description: Google第三方登錄* @author gaona* @param* @date Mar 13, 2017*/ @Controller public class LoginController {private static String client_id = "客戶端id";private static String client_secret = "客戶端秘鑰";private static String scope = "https://www.googleapis.com/auth/drive.metadata.readonly";private static String redirect_url = "http://gntina.iok.la/GoogleUserInfo";private static String code_url = "https://accounts.google.com/o/oauth2/v2/auth";private static String token_url = "https://www.googleapis.com/oauth2/v4/token";private static String user_url = "https://www.googleapis.com/oauth2/v2/userinfo";private static String verify_url = "https://www.googleapis.com/oauth2/v3/tokeninfo";//第一步:跳轉到登錄頁面@RequestMapping(value = "/login")public String toIndex(HttpServletRequest request) {return "google";}/*** @Title: Login* @Description: google登錄驗證后會重定向到此地址,并附帶訪問授權碼,不能為公開的ip地址* @author 此方法是用帶回的code換取accessToken,然后用accessToken換取用戶信息,這個地址就是在創建應用時定義的重定向地址* @return Object* @date Mar 23, 2017 10:37:38 AM* @throws*/@RequestMapping(value = "/GoogleUserInfo")@ResponseBodypublic static Object Login(HttpServletRequest request) {String code = request.getParameter("code");System.out.println(code);// String idToken = getGoogleAccessToken(code); // System.out.println(idToken); // JSONObject verifyToken = verifyToken(idToken); // System.out.println(verifyToken);String accessToken = getGoogleAccessToken(code);System.out.println(accessToken);JSONObject userInfo = getUserInfo(accessToken);System.out.println(userInfo);return userInfo;}/*** @throws Exception* @throws IOException* @Title: sendRedirect* @Description:發送授權請求* @author 第二步,在google.jsp中用戶登錄成功以后回跳轉到這個路徑,發送請求讓用戶授權,授權成功后重定向到/GoogleUserInfo,也就是創建應用時定義的重定向地址* @return String* @date Mar 24, 2017 3:11:36 PM* @throws*/@RequestMapping(value = "/sendRedirect")public void sendRedirect(HttpServletResponse response) throws Exception {// 隨機字符串,防止csrf攻擊String state = UUID.randomUUID() + "";Map<String, String> params = new HashMap<String, String>();params.put("client_id", client_id);params.put("redirect_uri", redirect_url);params.put("response_type", "code");params.put("scope", scope);params.put("access_type", "offline");params.put("state", state);params.put("include_granted_scopes", "true");String url = HttpClientUtil.getUrl(code_url, params);response.sendRedirect(url);}/*** @Title: getGoogleAccessToken* @Description: 獲取accessToken* @author 第三步,用重定向帶回來的code換取accessToken* @return String* @date Mar 25, 2017 10:25:00 AM* @throws*/public static String getGoogleAccessToken(String code) {HashMap<String, String> params = new HashMap<String, String>();params.put("client_id", client_id);params.put("redirect_uri", redirect_url);params.put("client_secret", client_secret);params.put("grant_type", "authorization_code");params.put("code", code);String[] responseResult = null;String accessToken =null;String idToken=null;try {responseResult = HttpClientUtil.getStringByPost(token_url, params,null);} catch (Exception e) {e.printStackTrace();}if (null != responseResult && responseResult[0].equals("200")) {String result = responseResult[1];JSONObject jsonObject = JSONObject.fromObject(result);accessToken = jsonObject.getString("access_token");idToken=jsonObject.getString("id_token");}return accessToken; // return idToken;}/*** @Title: getUserInfo* @Description: 獲取用戶信息* @author第四步,用accessToken獲取用戶信息* @return String* @date Mar 25, 2017 11:50:23 AM* @throws*/public static JSONObject getUserInfo(String accessToken) {HashMap<String, String> params = new HashMap<String,String>();params.put("access_token", accessToken);String[] responseResult =null;JSONObject userInfo=null;try {responseResult = HttpClientUtil.getStringByGet(user_url, params);} catch (Exception e) {e.printStackTrace();}if (null != responseResult && responseResult[0].equals("200")) {String result = responseResult[1];userInfo = JSONObject.fromObject(result); }return userInfo;}/*** @Title: verifyToken * @Description:驗證用戶token是否是來自本應用的請求,校驗aud和clientID是否相同 * @author第五步,驗證用戶是否來自你的應用,防刷,根據需要加到邏輯里* @return String* @date Mar 25, 2017 7:36:33 PM* @throws*/public static JSONObject verifyToken(String idToken){HashMap<String, String> params = new HashMap<String,String>();params.put("id_token", idToken);String[] responseResult =null;JSONObject verifyInfo=null;try {responseResult = HttpClientUtil.getStringByGet(verify_url, params);} catch (Exception e) {e.printStackTrace();}if (null != responseResult && responseResult[0].equals("200")) {String result = responseResult[1];verifyInfo = JSONObject.fromObject(result); }return verifyInfo;}} 流程明白了以后代碼就簡單了,實際上一共就3個接口,接口調用的方式用的http
③httpClienUtil(沒有截取全,只是部分代碼供參考,怕同學們因為調接口看暈了)
/*** 返回結果說明:* String[] result* result[0] = responseCode * result[1] = responseBody* @param url* @param params* @return UTF-8* @throws Exception*/public String[] getByUtf(String url, Map<String, String>params) throws Exception {if(params != null){StringBuffer buf = new StringBuffer();for(String key : params.keySet()){buf.append(key);buf.append("=");buf.append(URLEncoder.encode(params.get(key),"UTF-8"));buf.append("&");}String bufStr = buf.toString();if(StringUtils.isNotEmpty(bufStr)){String param = bufStr.substring(0, bufStr.lastIndexOf("&"));if (-1 == url.indexOf("?")) {url += "?" + param;} else {url += "&" + param;}}}GetMethod getmethod = new GetMethod(url);getmethod.getParams().setContentCharset(UTF_CHARSET);return httpRequest(getmethod,UTF_CHARSET);}
希望可以幫助到有需要的同學,有些東西了解個大概就往下走,不要深入,不然很容易陷進去出不來,我就是不幸的那個,陷進去出不來,出來了之后才發現看了一些沒用的浪費時間。
=====================================補充========================================
直接調用googleSDK的方式更簡單,直接上代碼:
google.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html><head><meta name="google-signin-scope" content="profile email"><meta name="google-signin-client_id" content="應用編號"><script src="https://apis.google.com/js/platform.js" async defer></script></head><body><div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div><a href="#" οnclick="signOut();">Sign out</a><script>function onSignIn(googleUser) {// 客戶端如果有需要的話可以通過profile來獲取用戶信息var profile = googleUser.getBasicProfile();// 傳回后臺驗證,并獲取useridvar id_token = googleUser.getAuthResponse().id_token;console.log("ID Token: " + id_token);var xhr = new XMLHttpRequest();xhr.open('POST', 'http://gntina.iok.la/googleVerify');xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');xhr.onload = function() {console.log('Signed in as: ' + xhr.responseText);};xhr.send('idtokenstr=' + id_token);};function signOut() {var auth2 = gapi.auth2.getAuthInstance();auth2.signOut().then(function () {console.log('User signed out.');});}</script></body> </html>Controller:驗證,獲取用戶信息
@RequestMapping(value = "/googleVerify", method = RequestMethod.POST)public void verifyToken(String idtokenstr) {System.out.println(idtokenstr);GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance()).setAudience(Collections.singletonList(client_id)).build();GoogleIdToken idToken = null;try {idToken = verifier.verify(idtokenstr);} catch (GeneralSecurityException e) {System.out.println("驗證時出現GeneralSecurityException異常");} catch (IOException e) {System.out.println("驗證時出現IOException異常");}if (idToken != null) {System.out.println("驗證成功.");Payload payload = idToken.getPayload();String userId = payload.getSubject(); // System.out.println("User ID: " + userId); // String email = payload.getEmail(); // boolean emailVerified = Boolean.valueOf(payload.getEmailVerified()); // String name = (String) payload.get("name"); // String pictureUrl = (String) payload.get("picture"); // String locale = (String) payload.get("locale"); // String familyName = (String) payload.get("family_name"); // String givenName = (String) payload.get("given_name");} else {System.out.println("Invalid ID token.");}}
總結
以上是生活随笔為你收集整理的Java实现Google第三方登录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java实现Facebook第三方登录
- 下一篇: Java面试必学-吐血推荐