نظرة عامة
الغرض: يصف هذا المستند وظائف OAuth 2.0 العامة التي تقدِّمها مكتبة برامج Google OAuth لـ Java. يمكنك استخدام هذه الوظائف للمصادقة والمصادقة لأي خدمة من خدمات الإنترنت.
للحصول على تعليمات حول استخدام GoogleCredential
لتنفيذ تفويض OAuth 2.0 مع
خدمات Google، يُرجى الاطّلاع على
استخدام OAuth 2.0 مع مكتبة عملاء Google API لـ Java.
الملخص: OAuth 2.0 هو عبارة عن مواصفات عادية للسماح للمستخدمين النهائيين بتفويض تطبيق العميل للوصول إلى الموارد المحمية من جهة الخادم. بالإضافة إلى ذلك، يشرح مواصفات الرمز المميّز لحامل OAuth 2.0 كيفية الوصول إلى هذه الموارد المحمية باستخدام رمز الدخول الممنوح أثناء عملية تفويض المستخدم النهائي.
لمعرفة التفاصيل، يُرجى الاطّلاع على وثائق JavaScript للحزم التالية:
- com.google.api.client.auth.oauth2 (من google-oauth-client)
- com.google.api.client.extensions.servelet.auth.oauth2 (من google-oauth-client-servlet)
- com.google.api.client.extensions.app Engine.auth.oauth2 (من google-oauth-client-app Engine)
تسجيل العميل
قبل استخدام مكتبة عميل Google OAuth لـ Java، قد تحتاج إلى تسجيل تطبيقك باستخدام خادم تفويض لتلقّي معرِّف العميل والرمز السري للعميل. (للحصول على معلومات عامة حول هذه العملية، يمكنك الاطّلاع على مواصفات تسجيل العميل.)
متجر بيانات الاعتماد وبيانات الاعتماد
بيانات الاعتماد
هي فئة مساعدة OAuth 2.0 آمنة للوصول إلى الموارد المحمية باستخدام رمز دخول. عند استخدام الرمز المميز لإعادة التحميل، يعمل Credential
أيضًا على إعادة تحميل رمز الدخول عند انتهاء صلاحية رمز الدخول باستخدام الرمز المميز لإعادة التحميل. على سبيل المثال، إذا كان لديك
رمز دخول من قبل، يمكنك تقديم طلب بالطريقة التالية:
public static HttpResponse executeGet( HttpTransport transport, JsonFactory jsonFactory, String accessToken, GenericUrl url) throws IOException { Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); return requestFactory.buildGetRequest(url).execute(); }
تحتاج معظم التطبيقات إلى الاحتفاظ برمز الدخول المميز الخاص ببيانات الاعتماد وإعادة تحميل الرمز المميز لتجنُّب إعادة التوجيه المستقبلية إلى صفحة التفويض في المتصفِّح. تم إيقاف واجهة برمجة التطبيقات CredentialStore في هذه المكتبة وستتم إزالتها في الإصدارات المستقبلية. بدلاً من ذلك، يمكنك استخدام واجهة DataStoreStore وDataStore مع StoredCredential، التي تتوفر من خلال مكتبة برامج Google HTTP لـ Java.
يمكنك استخدام إحدى عمليات التنفيذ التالية التي توفرها المكتبة:
- تحتفظ JdoDataStoreMan ببيانات الاعتماد باستخدام JDO.
- تحتفظ AppEngineDataStoreMan ببيانات الاعتماد باستخدام واجهة برمجة التطبيقات Google Data Engine Data Store.
- MemoryDataStoreMan "persists"بيانات الاعتماد في الذاكرة، وهي مفيدة فقط كمساحة تخزين قصيرة المدى طوال مدة العملية.
- تحتفظ FileDataStoreMan ببيانات الاعتماد في الملف.
مستخدمو Google App Engine:
AppEngineCredentialStore متوقّف وتتم إزالته.
نقترح استخدام AppEngineDataStoreMan مع StoredCredential. إذا كانت لديك بيانات اعتماد مُخزَّنة بالطريقة القديمة، يمكنك استخدام الطرق المساعدة الإضافية MigrateTo(AppEngineDataStoreMan) أو MigrateTo(DataStore) لنقل البيانات.
استخدام DataStoreCredentialUpdateListener وضبطه لبيانات الاعتماد باستخدام GoogleCredential.Builder.addUpdateListener(CredentialUpdateListener).
مسار رمز التفويض
استخدِم مسار رمز التفويض للسماح للمستخدم النهائي بمنح تطبيقك إذن الوصول إلى بياناته المحمية. يتم تحديد بروتوكول هذه العملية في مواصفات منح رمز التفويض.
ويتم تنفيذ هذه العملية باستخدام AuthorizeCodeFlow. الخطوات كالآتي:
- يسجِّل أحد المستخدمين الدخول إلى تطبيقك. عليك ربط هذا المستخدم بمعرّف مستخدم فريد لتطبيقك.
- يمكنك استدعاء authorizeCodeFlow.loadCredential(String)، استنادًا إلى رقم تعريف المستخدم، للتحقق مما إذا كانت بيانات اعتماد المستخدم\u39&29 معروفة مسبقًا. إذا كان الأمر كذلك، بهذا تكون قد انتهيت.
- إذا لم يكن الأمر كذلك، عليك استدعاء AuthorizeCodeFlow.newAuthorizeUrl() وتوجيه متصفح user's إلى صفحة التفويض حيث يمكنه منح تطبيقك حق الوصول إلى بياناته المحمية.
- بعد ذلك، يعيد متصفّح الويب التوجيه إلى عنوان URL لإعادة التوجيه باستخدام معلَمة "code;code"طلب البحث التي يمكن استخدامها بعد ذلك لطلب رمز دخول باستخدام AuthorizeCodeFlow.newTokenRequest(String).
- استخدِم AuthorizeCodeFlow.createAndStoreCredential(TokenResponse, String) لتخزين بيانات الاعتماد والحصول عليها للوصول إلى الموارد المحمية.
وبدلاً من ذلك، إذا كنت لا تستخدم AuthorizeCodeFlow، يمكنك استخدام الفئات ذات المستوى الأدنى:
- استخدِم DataStore.get(String) لتحميل بيانات الاعتماد من المتجر، استنادًا إلى رقم تعريف المستخدم.
- استخدام AuthorizeCodeRequestUrl لتوجيه المتصفِّح إلى صفحة التفويض
- يمكنك استخدام authorizeCodeResponseUrl لمعالجة استجابة التفويض وتحليل رمز التفويض.
- يمكنك استخدام AuthorizeCodeTokenRequest لطلب رمز دخول محتمل وربما رمز مميز لإعادة التحميل.
- إنشاء بيانات اعتماد جديدة وتخزينها باستخدام DataStore.set(String, V)
- يمكنك الوصول إلى الموارد المحمية باستخدام بيانات الاعتماد. تتم تلقائيًا إعادة تحميل رموز الدخول المنتهية الصلاحية باستخدام الرمز المميّز لإعادة التحميل، إذا كان ذلك منطبقًا. يجب استخدام DataStoreCredentialUpdateListener وضبطه لبيانات الاعتماد باستخدام Credential.Builder.addUploadListener(CredentialUpdateListener).
تدفق رمز تفويض الخدمة
توفّر هذه المكتبة دروسًا مساعدية من خِدْمَة الخدمات لتبسيط عملية تدفق رمز التفويض لحالات الاستخدام الأساسية. ما عليك سوى تقديم فئات فرعية ملموسة من AbstractAuthorizeCodeServlet وAbstractتفويضCodeCallbackServlet (من google-oauth-client-servlet) وإضافتها إلى ملف web.xml. تجدر الإشارة إلى أنك لا تزال بحاجة إلى الاعتناء بتسجيل دخول المستخدم في تطبيق الويب واستخراج رقم تعريف المستخدم.
نموذج التعليمات البرمجية:
public class ServletSample extends AbstractAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class ServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet { @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { // handle error } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
مسار رمز تفويض Google App Engine
يتشابه مسار رمز التفويض في App Engine تقريبًا مع تدفق رمز تفويض ServiceL، باستثناء أنه يمكننا الاستفادة من واجهة برمجة تطبيقات Java للمستخدمين. يحتاج المستخدم إلى تسجيل الدخول لتفعيل واجهة برمجة تطبيقات Java Users (المستخدمون في Java) للحصول على معلومات عن إعادة توجيه المستخدمين إلى صفحة تسجيل الدخول إذا لم يسبق لهم تسجيل الدخول، يُرجى الاطّلاع على الأمان والمصادقة (في web.xml).
ويتمثّل الاختلاف الأساسي مع حالة سيفلت في تقديم فئات فرعية ملموسة من AbstractAppEngineCodeCodeererlet وAbstractAppEngineCodeCodeServlet (من google-oauth-client-app Engine). وهم يوسّعون دروسًا نظرية مجرّدة ويستخدمون طريقة getUserId
نيابةً عنك باستخدام واجهة برمجة تطبيقات Java للمستخدمين. يُعدّ AppEngineDataStoreMan (من مكتبة برامج Google HTTP لـ Java خيارًا جيدًا للاحتفاظ ببيانات الاعتماد باستخدام واجهة برمجة التطبيقات Google App Engine Data Store.
نموذج التعليمات البرمجية:
public class AppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } } public class AppEngineCallbackSample extends AbstractAppEngineAuthorizationCodeCallbackServlet { @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { // handle error } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } }
مسار رمز تفويض سطر الأوامر
مثال مبسّط لرمز مأخوذ من dailymotion-cmdline-sample:
/** Authorizes the installed application to access user's protected data. */ private static Credential authorize() throws Exception { OAuth2ClientCredentials.errorIfNotSpecified(); // set up authorization code flow AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken .authorizationHeaderAccessMethod(), HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(TOKEN_SERVER_URL), new ClientParametersAuthentication( OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET), OAuth2ClientCredentials.API_KEY, AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE)) .setDataStoreFactory(DATA_STORE_FACTORY).build(); // authorize LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost( OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build(); return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); } private static void run(HttpRequestFactory requestFactory) throws IOException { DailyMotionUrl url = new DailyMotionUrl("https://api.dailymotion.com/videos/favorites"); url.setFields("id,tags,title,url"); HttpRequest request = requestFactory.buildGetRequest(url); VideoFeed videoFeed = request.execute().parseAs(VideoFeed.class); ... } public static void main(String[] args) { ... DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); final Credential credential = authorize(); HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { credential.initialize(request); request.setParser(new JsonObjectParser(JSON_FACTORY)); } }); run(requestFactory); ... }
تدفق البرنامج المستند إلى المتصفِّح
في ما يلي الخطوات النموذجية لتدفق البرنامج المستند إلى المتصفّح المُحدَّد في مواصفات المنح الضمنية:
- باستخدام BrowserClientRequestUrl، يمكنك إعادة توجيه متصفِّح المستخدم النهائي إلى صفحة التفويض حيث يمكن للمستخدم النهائي منح تطبيقك إمكانية الوصول إلى بياناته المحمية.
- يمكنك استخدام تطبيق JavaScript لمعالجة رمز الدخول الذي تم العثور عليه في جزء عنوان URL عند عنوان URL لإعادة التوجيه المسجّل مع خادم التفويض.
مثال على استخدام تطبيق ويب:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = new BrowserClientRequestUrl( "https://server.example.com/authorize", "s6BhdRkqt3").setState("xyz") .setRedirectUri("https://client.example.com/cb").build(); response.sendRedirect(url); }
رصد رمز دخول منتهي الصلاحية
وفقًا لمواصفات حامل الإصدار 2.0 من بروتوكول OAuth 2.0،
عندما يتم استدعاء الخادم للوصول إلى مورد محمي لديه رمز دخول منتهية الصلاحية، يستجيب الخادم عادةً برمز حالة HTTP 401 Unauthorized
كما يلي:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
ومع ذلك، يبدو أن هناك قدرًا كبيرًا من المرونة في المواصفات. للحصول على التفاصيل، راجِع وثائق موفّر OAuth 2.0.
هناك منهج بديل وهو التحقق من المعلمة expires_in
في
استجابة رمز الدخول.
يحدِّد هذا الإعداد القيمة الدائمة بالثواني للرمز المميّز للوصول، وهو عادةً ساعة. ومع ذلك، قد لا تنتهي صلاحية رمز الدخول المميز في نهاية هذه الفترة، وقد يستمر الخادم في السماح بالوصول. ولهذا السبب، ننصحك عادةً بانتظار رمز الحالة 401 Unauthorized
، بدلاً من افتراض انتهاء صلاحية الرمز استنادًا إلى الوقت المنقضي. يمكنك بدلاً من ذلك محاولة إعادة تحميل رمز الدخول قبل انتهاء صلاحيته بوقت قصير، وفي حال عدم توفّر خادم الرموز المميزة، يمكنك مواصلة استخدام رمز الدخول إلى حين تلقّي 401
. وهذه هي الاستراتيجية التي يتم استخدامها بشكل تلقائي في بيانات الاعتماد.
ويمكنك أيضًا استخدام رمز دخول جديد قبل كل طلب، إلا أنّ هذا الخيار يتطلّب طلب HTTP إضافيًا إلى خادم الرموز المميّزة في كل مرة، لذلك من المحتمل أن يكون هذا الإجراء خيارًا سيئًا من حيث السرعة واستخدام الشبكة. ويُفضَّل تخزين رمز الدخول المميَّز في مساحة تخزين آمنة ومستمرة لتقليل طلبات التطبيقات باستخدام رموز الدخول الجديدة. (بالنسبة إلى التطبيقات المثبّتة، يُعد التخزين الآمن مشكلة صعبة.)
يُرجى العِلم بأنّ رمز الدخول قد يكون غير صالح لأسباب أخرى غير انتهاء الصلاحية، على سبيل المثال، إذا أبطل المستخدم الرمز المميّز صراحةً، لذا تأكّد من أنّ رمز معالجة الأخطاء قوي. عندما تكتشف أنّ رمزًا مميّزًا لم يعد صالحًا، على سبيل المثال، إذا انتهت صلاحيته أو تم إبطاله، عليك إزالة الرمز المميّز للوصول من مساحة التخزين. على سبيل المثال، يجب استخدام جهاز AccountManager.invalidAuthToken على نظام التشغيل Android.