9.6. Social Login
Вход через социальные сети, или social login, это разновидность single sign-on, которая позволяет использовать данные для входа в социальные сети, такие как Facebook, Twitter или Google+, для входа в приложения CUBA вместо того, чтобы создавать пользователя в приложении напрямую.
В этом примере мы рассмотрим, как можно войти в приложение, используя аккаунт на 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.