domingo, 19 de febrero de 2017

Spring Social: SignIn y SignUp


En este post intentaremos explicar de la forma más breve y clara como utilizar Spring Social en tu aplicación. Y como permitir logarse a los usuarios de una red social (en este caso Facebook), e incluso registrarlos en la aplicación, si es la primera vez que acceden. 

El post se centrará exclusivamente en el funcionamiento de Spring Social (pudiendose explicar alguna otra sección si alguien lo pide). El código lo podéis encontrar aquí: Github
  • 1º paso: Obtener un ID para tu aplicación
Debemos acceder a Facebook Developers y crear una aplicación asociada a nuestro usuario. A esta aplicación le tenemos que dar permiso para autenticación con Facebook, y entre los datos a solicitar debemos pedirle el 'email'. 

El identificador de facebook y su clave secreta se deben añadir al fichero application.properties (el fichero de configuración de Spring Boot). 
  • 2º paso: Indicar a Spring Security que vamos a hacer uso de Spring Social
Utilizaremos Spring Security para que maneje la autenticación de usuarios, y a que páginas se puede o no acceder. Con la siguiente configuración, permitiremos que los usuarios de redes sociales se integren dentro del ciclo de autenticación.

protected void configure( final HttpSecurity http ) throws Exception {
http.authorizeRequests()
//... mas configuraciones aqui
.and().apply( new SpringSocialConfigurer().postLoginUrl( "/" ).alwaysUsePostLoginUrl( true ) );
}
  • 3º paso: Configurar Spring Social
Debemos crear una clase que implemente 'SocialConfigurer'. Esta clase contendrá la anotación '@EnableSocial' que permitirá añadir esta configuración a la propia de Spring MVC. Además tendrá tres métodos esenciales. 
- addConnectionFactories: No necesitaremos configurarlo al estar utilizando Spring Boot y haber configurado anteriormente el fichero 'application.properties' . 
- getUserIdSource: Donde indicaremos que campo usaremos como identificador del usuario. La implementación por defecto que se suele hacer, nos permite indicar que será el 'username'. Esto nos dará algún quebradero de cabeza mas adelante, puesto que Facebook no cuenta con un username como tal. 
- getUsersConnectionRepository: Donde indicaremos que la información de las conexiones será persistida en BBDD y que en caso de que el usuario no se haya logado nunca se redirija a un clase concreta (CustomConnectionSignUp) para registrarlo en la aplicación. 

@Configuration
@EnableSocial
public class SocialConfig implements SocialConfigurer {
 @Autowired
 private DataSource dataSource;
 @Autowired
 private UserDao dao;
 @Override
 public void addConnectionFactories( final ConnectionFactoryConfigurer connectionFactoryConfigurer, final Environment environment ) {
 }
 @Override
 public UserIdSource getUserIdSource() {
  return new AuthenticationNameUserIdSource();
 }
 @Override
 public UsersConnectionRepository getUsersConnectionRepository( final ConnectionFactoryLocator connectionFactoryLocator ) {
  JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository( dataSource, connectionFactoryLocator, Encryptors.noOpText() );
  repository.setConnectionSignUp( new CustomConnectionSignUp( dao ) );
  return repository;
 }
}

La tabla donde se persitirán los datos es 'userconnection' y almacenará la información de conexión de los usuarios a través de sus redes sociales. Si encuentra al usuario en esta tabla, procederá a logarlo en la aplicación y en caso contrario procederá a registrarlo y posteriormente logarlo. El esquema de la misma se encuentra también en el código de Github. 
  • 4º Paso: Registrar usuario de red social automáticamente
Ya hemos configurado la clase que permite el registro, ahora nos queda implementarla. Básicamente nuestra labora será obtener los datos de Facebook, crear un registro del usuario de la aplicación con ellos y devolver el identificador de dicho usuario. Como ya indicamos anteriormente, un usuario de Facebook no tiene 'username' como tal, pero podemos utilizar su correo electrónico como identificador. 

public class CustomConnectionSignUp implements ConnectionSignUp {
 private final UserDao userDao;
 public CustomConnectionSignUp( final UserDao usersDao ) {
  userDao = usersDao;
 }
 @Override
 public String execute( final Connection<?> connection ) {
  Facebook facebook = (Facebook) connection.getApi();
  String[] fields = { "email", "name", "first_name", "last_name" };
  User userProfile = facebook.fetchObject( "me", User.class, fields );
  // seteamos a nulo para poder insertar AI MYSQL
  userProfile.setId( null );
  // generamos password
  userProfile.setPassword( createEncryptPassword() );
  // indicamos como username el correo
  userProfile.setUsername( userProfile.getEmail() );
  userProfile = userDao.save( userProfile );
  return userProfile.getEmail();
 }
 private String createEncryptPassword() {
  return new BCryptPasswordEncoder().encode( RandomStringUtils.randomAlphanumeric( 17 ) );
 }
}
  • 5º Paso: Identificar al usuario de la red social en la aplicación y logarlo
Deberemos crear una clase que implemente 'SocialUserDetailsService' y que permita logar al usuario proveniente de la red social. El parámetro 'userId' que llega al método 'loadUserByUserId', será el valor devuelto por la clase 'CustomConnectionSignUp'. Y el objeto UserDetails devuelto, será el asociado a la sesión del usuario a través de Spring Security.

El código de Github puede ser algo diferente, pero es porque para realizar esta parte nos hemos apoyado en 'UserDetailsService'. Y de paso nos sirve su implementación para realizar el acceso del usuario a través de usuario y contraseña.

@Service
public class CustomSocialUsersDetailService implements SocialUserDetailsService {
 @Autowired
 private UserDao userDao;
 public UserDetails loadUserByUsername( final String username ) throws UsernameNotFoundException {
  User user = userDao.findByUsernameOrEmail( username, username );
  if( user == null ) {
   throw new UsernameNotFoundException( "No user present with username/email: " + username );
  } else {
   List<String> userRoles = new ArrayList<>();
   userRoles.add( "USER" );
   return new CustomUserDetails( user, userRoles );
  }
 }
 @Override
 public SocialUserDetails loadUserByUserId( final String userId ) throws UsernameNotFoundException, DataAccessException {
  UserDetails userDetails = loadUserByUsername( userId );
  return new SocialUser( userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities() );
 }
}
  • 6º Paso: Habilitar enlace para logarse a través de Facebook
Spring social pone a nuestro alcance diferentes método de conexión con las redes sociales y que son manejados de manera automática por el. Para nuestro caso, y para poder autenticar al usuario en nuestro sistema a través de la red social disponemos de 'auth'. Si creamos un enlace o un formulario que apunte a la dirección '/auth/{providerId}', donde '{providerId}' es el identificador de nuestra red social devidamente configurada, podremos autenticarnos a través de ella. Hay que tener en cuenta que no vale cualquier red social.


<form action="${pageContext.request.contextPath}/auth/facebook" method="POST">
 <input type="hidden" name="scope" value="email, public_profile" />
 <p><button class="btn btn-default col-md-12 col-sm-12 col-xs-12 btnSignFacebook" type="submit"><span class="fb-icon"></span><spring:message code="b.signFacebook" /></button></button></p>
 <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>

No hay comentarios:

Publicar un comentario