How to sign up new user via Spring Social Facebook

2019-06-22 11:48发布

I am having a website (Angular App) which I can reach on https://localhost:4200 and a server (pure REST API) that is on https://localhost:8443.

Since the REST endpoints are secured, a user must login on my server. Obviously I want users to be able to signup using Facebook and further communicate with my server after login.


According to the docs

POST /signin/{providerId} - Initiates the sign in flow.

that is why there is a button that does just this:

<form ngNoForm name="fb_signin" id="fb_signin" action="https://localhost:8443/signin/facebook" method="POST">
  <input type="hidden" name="scope" value="email">
  <button type="submit">SIGNING</button>
</form>

From there, everything works fine for some time. The user gets redirected to Facebook's authorization page where the authorization button gets clicked. After that the user gets redirected to https://localhost:8443/signin/facebook.


It seems that per default, in case the user is unknown, there will be a another redirect to https://localhost:8443/signup (see docs)

If the provider user ID matches more than one existing connection, ProviderSignInController will redirect to the application’s sign in URL to offer the user a chance to sign in through another provider or with their username and password. The request to the sign in URL will have an "error" query parameter set to "multiple_users" to indicate the problem so that the page can communicate it to the user. The default sign in URL is "/signin" (relative to the application root), but can be customized by setting the signInUrl property.

On my serer this looks like this (borrowed from a sample application):

@RequestMapping(value = "/signup", method = RequestMethod.GET)
public SignUpForm signupForm(WebRequest request) {
    Connection<?> connection = providerSignInUtils.getConnectionFromSession(request);
    if (connection != null) {
        request.setAttribute("message", new Message(MessageType.INFO, "Your " + StringUtils.capitalize(connection.getKey().getProviderId()) + " account is not associated with a Spring Social Showcase account. If you're new, please sign up."), WebRequest.SCOPE_REQUEST);
        return SignUpForm.fromProviderUser(connection.fetchUserProfile());
    } else {
        return new SignUpForm();
    }
}

And here are my issues:

First of all I need to know what I am supposed to do at this endpoint. The user authorized my app, but my server does not know the user yet so do I

  • connection.fetchUserProfile() and save the new user to my database
  • something else?

Second, I do not know how I am supposed to redirect back to my website from here which, as explained, lies on https://localhost:4200. But of course, my server does not know that.

Is there a chance somebody can guide me through this?


This is the SocialConfig

@Configuration
@EnableSocial
public class SocialConfig implements SocialConfigurer {

    private final static Logger LOGGER = LogManager.getLogger(SocialConfig.class);

    @Value("${spring.social.facebook.appId}")
    String facebookAppId;

    @Value("${spring.social.facebook.appSecret}")
    String facebookSecret;

    @Autowired
    private DataSource dataSource;

    @Bean
    @Scope(value="singleton", proxyMode = ScopedProxyMode.INTERFACES)
    public ConnectionFactoryLocator connectionFactoryLocator() {
        ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();

        registry.addConnectionFactory(new FacebookConnectionFactory(
                facebookAppId,
                facebookSecret
        ));

        return registry;
    }

    @Bean
    @Scope(value="singleton", proxyMode=ScopedProxyMode.INTERFACES)
    public UsersConnectionRepository usersConnectionRepository() {
        return new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator(), Encryptors.noOpText());
    }

    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
        LOGGER.debug("Adding connection factories");
        cfConfig.addConnectionFactory(new FacebookConnectionFactory(
                env.getProperty("facebook.clientId"),
                env.getProperty("facebook.clientSecret")));

    }

    @Override
    public UserIdSource getUserIdSource() {
        return new AuthenticationNameUserIdSource();
    }

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        return new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
    }

    @Bean
    public ProviderSignInController providerSignInController(
            ConnectionFactoryLocator connectionFactoryLocator,
            UsersConnectionRepository usersConnectionRepository) {
        ProviderSignInController controller = new ProviderSignInController(
                connectionFactoryLocator,
                usersConnectionRepository,
                new SimpleSignInAdapter(new HttpSessionRequestCache()));

        return controller;
    }

    @Bean
    public RequestCache requestCache() {
        return new HttpSessionRequestCache();
    }

    @Bean
    public SignInAdapter signInAdapter() {
        return new SimpleSignInAdapter(new HttpSessionRequestCache());
    }

}

Maven dependencies related to Spring Social:

<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-facebook</artifactId>
    <version>3.0.0.M1</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-security</artifactId>
    <version>2.0.0.M4</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-core</artifactId>
    <version>2.0.0.M2</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-config</artifactId>
    <version>2.0.0.M2</version>
</dependency>

2条回答
放荡不羁爱自由
2楼-- · 2019-06-22 12:33

After that the user gets redirected to https://localhost:8443/signin/facebook.

I think there is another best way of doing so .As your angular webapp (https://localhost:4200) comprises of webLayer so directly redirecting via form action is not a better way.You can use Proxy Routing click here

Now in case of you are using you are using proxy routing u need to configure your proxy port (8443) by command.

ng serve –proxy-config proxy.conf.json
where proxy.conf.json contain your configuration https://localhost:8443/

then you need to first authenticate your each user/new user to your server(service layer) & then pass every request through user service layer controller .This will lead to pass every request through your server and it solve your problem.

The user authorized my app, but my server does not know the user yet so do I

查看更多
仙女界的扛把子
3楼-- · 2019-06-22 12:40

This is my signup PoC code , FYI

@Controller
class HomeController : AbstractController() {

  private var sessionStrategy: SessionStrategy = HttpSessionSessionStrategy()

  @Inject
  private lateinit var connectionFactoryLocator: ConnectionFactoryLocator

  @Inject
  private lateinit var usersConnectionRepository: FoodUsersConnectionRepository

  @RequestMapping("/")
  fun index(): String {
    logger.info("index")
    return "index"
  }

  @RequestMapping("/signup")
  fun signup(nativeWebRequest: NativeWebRequest) : String {
    sessionStrategy.getAttribute(nativeWebRequest , ProviderSignInAttempt.SESSION_ATTRIBUTE)
      .takeIf { it != null }
      .also { it ->
        val attempt = it as ProviderSignInAttempt
        val connection = attempt.getConnection(connectionFactoryLocator)
        logger.info("conn.key = {}" , connection.key)
        val user = userDao.save(User(RandomStringUtils.randomAlphanumeric(8) , RandomStringUtils.randomAlphanumeric(8)))
        val connRepo: ConnectionRepository = usersConnectionRepository.createConnectionRepository(user.id.toString())
        connRepo.addConnection(connection)
      }
    return "signup"
  }

}

Points to notes :

I retrieve the connection object set in ProviderSignInController.handleSignIn() , it contains freshy (not mapped in db) providerId/providerUserId pair. And I create a new local user , give it random username/password pair , and link the new providerId/providerUserId to the local user.

This is a simple PoC code , it should be modified to conform to your business logic.

Maybe you can display a form for user to fill-in his nickname or address or something. That's depend on your business logic.

查看更多
登录 后发表回答