6.7. Social Login
Вход через социальные сети, или social login, это разновидность single sign-on, которая позволяет использовать данные для входа в социальные сети, такие как Facebook, Twitter или Google+, для входа в приложения CUBA вместо того, чтобы создавать пользователя в приложении напрямую.
|  
       Смотрите также руководство Anonymous Access and Social Login. В руководстве приведен пример настройки открытого доступа к CUBA-приложению, а также реализации пользовательского входа в приложение с помощью учетной записи Google, Facebook или GitHub.  |  
    
В этом примере мы рассмотрим, как можно войти в приложение, используя аккаунт на Facebook. В Facebook используется механизм авторизации OAuth2, более подробно о его использовании вы можете узнать из документации по Facebook API и Facebook Login Flow: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow.
Исходный код проекта из этого примера доступен на GitHub, ниже приведены ключевые моменты реализации social login.
-  
Чтобы подключить приложение к Facebook, необходимо создать для него App ID (уникальный идентификатор приложения) и App Secret (своего рода пароль для аутентификации запросов, поступающих от приложения на серверы Facebook). Следуя инструкции, создайте эти значения и затем зарегистрируйте их в файле
app.propertiesв модуле core в свойствах приложенияfacebook.appIdиfacebook.appSecretсоответственно, например:facebook.appId = 123456789101112 facebook.appSecret = 123456789101112abcde131415fghi16Также необходимо зарегистрировать URL, который вы указали при регистрации приложения на Facebook, в свойстве приложения cuba.webAppUrl в модулях core и web, к примеру:
cuba.webAppUrl = http://cuba-fb.test:8080/app -  
Расширьте окно входа в систему и добавьте кнопку для входа через социальную сеть. По нажатию этой кнопки будет вызываться метод
loginFacebook()- наша точка входа в процедуру social login. -  
Чтобы использовать учётные записи пользователей Facebook в своём приложении, необходимо добавить новое поле к стандартной учётной записи пользователя CUBA. Расширьте сущность
Userи добавьте строковый атрибутfacebookId:@Column(name = "FACEBOOK_ID") protected String facebookId; -  
Создайте сервис, который будет искать пользователя приложения в базе данных по переданному
facebookId, и если таковой не найден, то создавать его на лету:public interface SocialRegistrationService { String NAME = "demo_SocialRegistrationService"; User findOrRegisterUser(String facebookId, String email, String name); }@Service(SocialRegistrationService.NAME) public class SocialRegistrationServiceBean implements SocialRegistrationService { @Inject private Metadata metadata; @Inject private Persistence persistence; @Inject private Configuration configuration; @Override @Transactional public User findOrRegisterUser(String facebookId, String email, String name) { EntityManager em = persistence.getEntityManager(); TypedQuery<SocialUser> query = em.createQuery("select u from sec$User u where u.facebookId = :facebookId", SocialUser.class); query.setParameter("facebookId", facebookId); query.setViewName(View.LOCAL); SocialUser existingUser = query.getFirstResult(); if (existingUser != null) { return existingUser; } SocialRegistrationConfig config = configuration.getConfig(SocialRegistrationConfig.class); Group defaultGroup = em.find(Group.class, config.getDefaultGroupId(), View.MINIMAL); SocialUser user = metadata.create(SocialUser.class); user.setFacebookId(facebookId); user.setEmail(email); user.setName(name); user.setGroup(defaultGroup); user.setActive(true); user.setLogin(email); em.persist(user); return user; } } -  
Создайте сервис для реализации логики входа. В данном примере это сервис FacebookService, содержащий два метода:
getLoginUrl()иgetUserData().-  
getLoginUrl()генерирует URL для входа на основании URL приложения и типа ответа OAuth2 (code, access token или оба; более подробно о параметреresponse_typeсм. в документации Facebook API). Исходный код этого метода можно посмотреть в файле FacebookServiceBean.java. -  
getUserData()будет искать пользователя Facebook по параметрам, переданным в URL и в коде, и вернёт данные существующего пользователя или создаст нового. В этом примере из пользовательских данных нам нужны id, name и email, id будет соответствовать атрибутуfacebookId, который мы создали ранее. 
 -  
 -  
Определите свойства приложения
facebook.fieldsиfacebook.scopeв файлеapp.propertiesмодуля core:facebook.fields = id,name,email facebook.scope = email -  
Вернёмся к методу
loginFacebook()в контроллере расширенного окна входа. Код контроллера целиком вы можете найти в файле ExtAppLoginWindow.java.В этом методе мы добавим к текущей сессии обработчик запроса, затем сохраним текущий URL и перенаправим пользователя на экран авторизации Facebook в браузере:
private RequestHandler facebookCallBackRequestHandler = this::handleFacebookCallBackRequest; private URI redirectUri; @Inject private FacebookService facebookService; @Inject private GlobalConfig globalConfig; public void loginFacebook() { VaadinSession.getCurrent() .addRequestHandler(facebookCallBackRequestHandler); this.redirectUri = Page.getCurrent().getLocation(); String loginUrl = facebookService.getLoginUrl(globalConfig.getWebAppUrl(), OAuth2ResponseType.CODE); Page.getCurrent() .setLocation(loginUrl); }В методе
handleFacebookCallBackRequest()будет обработан обратный вызов после формы авторизации Facebook. Во-первых, используем экземплярUIAccessor, чтобы зафиксировать состояние UI, пока будет обрабатываться запрос на вход.Затем с помощью
FacebookServiceполучим email и id учётной записи Facebook. После этого найдём соответствующего пользователя CUBA по егоfacebookIdили создадим нового на лету.Далее будет совершен вход в приложение, будет создана новая сессия от лица этого пользователя и обновлен UI. Теперь мы можем удалить обработчик обратного вызова Facebook, так как процедура аутентификации закончена.
public boolean handleFacebookCallBackRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException { if (request.getParameter("code") != null) { uiAccessor.accessSynchronously(() -> { try { String code = request.getParameter("code"); FacebookUserData userData = facebookService.getUserData(globalConfig.getWebAppUrl(), code); User user = socialRegistrationService.findOrRegisterUser( userData.getId(), userData.getEmail(), userData.getName()); App app = App.getInstance(); Connection connection = app.getConnection(); Locale defaultLocale = messages.getTools().getDefaultLocale(); connection.login(new ExternalUserCredentials(user.getLogin(), defaultLocale)); } catch (Exception e) { log.error("Unable to login using Facebook", e); } finally { session.removeRequestHandler(facebookCallBackRequestHandler); } }); ((VaadinServletResponse) response).getHttpServletResponse(). sendRedirect(ControllerUtils.getLocationWithoutParams(redirectUri)); return true; } return false; } 
Теперь при нажатии кнопки Facebook на экране входа приложение запросит разрешение на использование учётных данных пользователя Facebook, и если разрешение будет получено, пользователь после логина будет перенаправлен на главную страницу приложения.
Вы можете реализовать свой механизм входа при помощи интерфейсов LoginProvider, HttpRequestFilter и обработчиков событий, как это описано в разделе Процесс входа в Web Client.