Filtro de autenticación social de Spring para endpoints REST sin estado que usan Facebook Token para autenticación

Me gustaría usar Facebook Tokens para autenticar mi back-end REST usando Spring Security . ¿Podría explicar cómo puedo integrar esta seguridad a mi aplicación Spring?

Me gustaría usar la misma administración de usuarios que Spring Social Security. Tabla de UserConnection y tabla de usuario local.

Puede descargar la muestra del código desde:

https://github.com/ozgengunay/FBSpringSocialRESTAuth

Hemos estado buscando una solución “Spring” que asegure nuestros backends REST usando Facebook OAuth Token que los clientes de REST ya tienen a mano. Por ejemplo: tiene una aplicación móvil con Facebook Connect SDK implementada en la aplicación y, por otro lado, tiene un backend que proporciona API REST. Desea autenticar las llamadas a la API REST con Facebook OAuth Token. La solución se da cuenta de este escenario.

Desafortunadamente, Spring Social Security Framework solo protege sus solicitudes HTTP con estado, no su back-end REST sin estado.

Esta es una extensión del marco de seguridad social de spring que consta de un componente: FacebookTokenAuthenticationFilter. Este filtro intercepta todas las llamadas REST. Los clientes deben enviar Facebook OAuth Token en la url como parámetro “input_token” en cada solicitud, ya que las API REST son steteless por naturaleza. El filtro busca este token y lo valida mediante la llamada de API Api “debug_token”. Si el token está validado, el filtro intenta hacer coincidir al usuario con el sistema de administración de usuarios local. Si aún no hay ningún usuario registrado, el filtro registra al usuario como un nuevo usuario.

Puede usar este filtro junto con SocialAuthenticationFilter estándar de Spring Social Security si tiene otros servicios además de su API REST como un backend web. Entonces puedes usar el mismo sistema de administración de usuarios.

1) Cree su tabla de usuarios de la siguiente manera en MYSQL:

 CREATE TABLE IF NOT EXISTS `user` ( `id` varchar(50) NOT NULL, `email` varchar(255) NOT NULL COMMENT 'unique', `first_name` varchar(255) NOT NULL, `last_name` varchar(255) NOT NULL, `password` varchar(255) DEFAULT NULL, `role` varchar(255) NOT NULL, `sign_in_provider` varchar(20) DEFAULT NULL, `creation_time` datetime NOT NULL, `modification_time` datetime NOT NULL, `status` varchar(20) NOT NULL COMMENT 'not used', PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`) ); 

2) Configure su origen de datos en context.xml:

context.xml en tomcat:

  

3) Configuración de Spring: configuramos la seguridad de spring para interceptar las URL que comienzan con “protected” por FacebookTokenAuthenticationFilter para la autenticación. La autorización se realizará mediante el rol “ROLE_USER_REST_MOBILE”.

                             

4) Todas las solicitudes de REST sin estado serán interceptadas por FacebookTokenAuthenticationFilter para autenticar las solicitudes utilizando un token de Facebook válido. Comprueba si el token de Facebook es válido. Si el token de Facebook no es válido, se denegará la solicitud. Si el token de Facebook es válido, el filtro intentará autenticar la solicitud a través de SimpleSocialUserDetailsService. Si los datos de conexión del usuario y del usuario no están disponibles, se crea un nuevo usuario (a través de UserService) y UserConnection.

 private Authentication attemptAuthService(...) { if (request.getParameter("input_token") == null) { throw new SocialAuthenticationException("No token in the request"); } URIBuilder builder = URIBuilder.fromUri(String.format("%s/debug_token", "https://graph.facebook.com")); builder.queryParam("access_token", access_token); builder.queryParam("input_token", request.getParameter("input_token")); URI uri = builder.build(); RestTemplate restTemplate = new RestTemplate(); JsonNode resp = null; try { resp = restTemplate.getForObject(uri, JsonNode.class); } catch (HttpClientErrorException e) { throw new SocialAuthenticationException("Error validating token"); } Boolean isValid = resp.path("data").findValue("is_valid").asBoolean(); if (!isValid) throw new SocialAuthenticationException("Token is not valid"); AccessGrant accessGrant = new AccessGrant(request.getParameter("input_token"), null, null, resp.path("data").findValue("expires_at").longValue()); Connection connection = ((OAuth2ConnectionFactory) authService.getConnectionFactory()) .createConnection(accessGrant); SocialAuthenticationToken token = new SocialAuthenticationToken(connection, null); Assert.notNull(token.getConnection()); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || !auth.isAuthenticated()) { return doAuthentication(authService, request, token); } else { addConnection(authService, request, token); return null; } } 

5) Otras secciones importantes en el proyecto:

Usuario: Entidad que mapea la tabla ‘usuario’.

 @Entity @Table(name = "user") public class User extends BaseEntity { @Column(name = "email", length = 255, nullable = false, unique = true) private String email; @Column(name = "first_name", length = 255, nullable = false) private String firstName; @Column(name = "last_name", length = 255, nullable = false) private String lastName; @Column(name = "password", length = 255) private String password; @Column(name = "role", length = 255, nullable = false) private String rolesString; @Enumerated(EnumType.STRING) @Column(name = "sign_in_provider", length = 20) private SocialMediaService signInProvider; ... } 

UserRepository: Repositorio JPA de Spring Data que nos permitirá ejecutar operaciones CRUD en la entidad ‘Usuario’.

 public interface UserRepository extends JpaRepository { public User findByEmailAndStatus(String email,Status status); public User findByIdAndStatus(String id,Status status); } 

UserService: este servicio de spring se usará para crear una nueva cuenta de usuario insertando datos en la tabla de ‘usuario’.

 @Service public class UserService { private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); @Autowired private UserRepository repository; @Transactional public User registerNewUserAccount(RegistrationForm userAccountData) throws DuplicateEmailException { LOGGER.debug("Registering new user account with information: {}", userAccountData); if (emailExist(userAccountData.getEmail())) { LOGGER.debug("Email: {} exists. Throwing exception.", userAccountData.getEmail()); throw new DuplicateEmailException("The email address: " + userAccountData.getEmail() + " is already in use."); } LOGGER.debug("Email: {} does not exist. Continuing registration.", userAccountData.getEmail()); User registered =User.newEntity(); registered.setEmail(userAccountData.getEmail()); registered.setFirstName(userAccountData.getFirstName()); registered.setLastName(userAccountData.getLastName()); registered.setPassword(null); registered.addRole(User.Role.ROLE_USER_WEB); registered.addRole(User.Role.ROLE_USER_REST); registered.addRole(User.Role.ROLE_USER_REST_MOBILE); if (userAccountData.isSocialSignIn()) { registered.setSignInProvider(userAccountData.getSignInProvider()); } LOGGER.debug("Persisting new user with information: {}", registered); return repository.save(registered); } .... } 

SimpleSocialUserDetailsService: este servicio de Spring será utilizado por SocialAuthenticationProvider para autenticar userId del usuario.

 @Service public class SimpleSocialUserDetailsService implements SocialUserDetailsService { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSocialUserDetailsService.class); @Autowired private UserRepository repository; @Override public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException, DataAccessException { LOGGER.debug("Loading user by user id: {}", userId); User user = repository.findByEmailAndStatus(userId, Status.ENABLED); LOGGER.debug("Found user: {}", user); if (user == null) { throw new UsernameNotFoundException("No user found with username: " + userId); } ThingabledUserDetails principal = new ThingabledUserDetails(user.getEmail(),user.getPassword(),user.getAuthorities()); principal.setFirstName(user.getFirstName()); principal.setId(user.getId()); principal.setLastName(user.getLastName()); principal.setSocialSignInProvider(user.getSignInProvider()); LOGGER.debug("Found user details: {}", principal); return principal; } } 

Puede descargar la muestra del código desde:

https://github.com/ozgengunay/FBSpringSocialRESTAuth