3

I am developing an app whose frontend is written using React.js and the backend REST API is written using the Spring framework. I wanted to add social logins to my website, so after days of googling and research, I understood that OAuth2 is the solution. I came to know that the frontend should handle getting the authorization token from the Resource Server(Facebook here) and my backend(java) should validate that token and connect with Facebook to get an access token. Then that access token should be stored in my database along with the user details(e.g email).

Here is my requirement, once the user clicks on the "Continue with Facebook" button, my app should create there account in my own database using details - email and Name(the signup feature). And later whenever they click on this button again, they will be logged in not sign up. The way other websites handle it.

As of now, I have the button working in my app, which brings me the authorization token from Facebook.

Can someone please guide me the path I should follow here.

Also, any special attention to some error handling I should follow.

4

1 回答 1

8

这是使用 Spring Boot 作为由 Spring Data JPA 和 Spring Security 支持的 REST API 的一般方法,适用于 iOS 并ember.js一起工作。可能有库和你不能使用的东西,但我只是要概述基本流程。

  1. 您的用户对象需要一对一映射到 Facebook 帐户。最佳实践将涉及加密authToken存储在数据库中之前
@Entity
class FacebookAccount {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    Long id

    String facebookUserId
    String authToken

    @OneToOne
    @JoinColumn(name="user_id")
    User user
}
@Entity
class User{

...
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    FacebookAccount facebookAccount
}
  1. 使用facebook Javascript SDK获取用户访问令牌和用户的 Facebook 用户 ID。在成功的情况下,您会在您的 react 应用程序中收到来自 facebook 的回复,如下所示:
{
    status: 'connected',
    authResponse: {
        accessToken: '...',
        expiresIn:'...',
        reauthorize_required_in:'...'
        signedRequest:'...',
        userID:'...'
    }
}
  1. 使用在步骤 2 中收到的信息点击一些登录端点,例如/login/facebook. 我无法预测您的应用程序的结构。在我的应用程序中,此代码由实现GenericFilterBean. 我传递了一个X-Auth-Facebook带有令牌的标题。

  2. 验证令牌。我在一个在方法AuthenticationProvider内实现的类中执行此Authentication authenticate(Authentication authentication) throws AuthenticationException操作。此类将需要您的 App 的 Access TokenaccessToken和用户的 Token userAccessToken

URIBuilder builder = URIBuilder.fromUri(String.format("%s/debug_token", "https://graph.facebook.com"))
builder.queryParam("access_token", accessToken)
builder.queryParam("input_token", userAccessToken)
URI uri = builder.build()
RestTemplate restTemplate = new RestTemplate()

JsonNode resp = null
try {
    resp = restTemplate.getForObject(uri, JsonNode.class)
} catch (HttpClientErrorException e) {
    throw new AuthenticationServiceException("Error requesting facebook debug_token", e)
}

Boolean isValid = resp.path("data").findValue("is_valid").asBoolean()
if (!isValid)
    throw new BadCredentialsException("Token not valid")

String fbookUserId = resp.path("data").findValue("user_id").textValue()
if (!fbookUserId)
    throw new AuthenticationServiceException("Unable to read user_id from facebook debug_token response")

// spring data repository that finds the FacebookAccount by facebook user id
FacebookAccount fbookAcct = facebookAccountRepository.findByFacebookUserId(fbookUserId)
if(!fbookAcct){
    // create your user here
    // save the facebook account as well
} else{
  // update the existing users token
  fbookAcct.authToken = userAccessToken
  facebookAccountRepository.save(fbookAcct)
}
// finish the necessary steps in creating a valid Authentication

然后,我个人创建一个令牌,供客户在访问我的 API 时使用(而不是让他们继续通过所有请求传递 facebook 令牌)。

我还需要更多用户提供的信息来创建用户(选择的用户名、同意条款和条件等)。所以我的实际实现抛出了一个EntityNotFoundException而不是创建用户,然后我的客户使用它来弹出一个注册表单,该表单只提供我无法从 facebook 获取的字段。在从客户端提交此内容时,我/signup/facebook使用 facebook 令牌和创建用户所需的内容点击了我的端点。我从 facebook 获取个人资料并创建用户(在此过程中自动记录他们)。

编辑:如果您想使用 Spring 0Auth,您可以按照示例创建 Spring 2 Oauth Rest Template

@Bean
public OAuth2ProtectedResourceDetails facebook() {
    AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
    details.setId("facebook");
    details.setClientId("233668646673605");
    details.setClientSecret("33b17e044ee6a4fa383f46ec6e28ea1d");
    details.setAccessTokenUri("https://graph.facebook.com/oauth/access_token");
    details.setUserAuthorizationUri("https://www.facebook.com/dialog/oauth");
    details.setTokenName("oauth_token");
    details.setAuthenticationScheme(AuthenticationScheme.query);
    details.setClientAuthenticationScheme(AuthenticationScheme.form);
    return details;
}

@Bean
public OAuth2RestTemplate facebookRestTemplate(OAuth2ClientContext clientContext) {
    OAuth2RestTemplate template = new OAuth2RestTemplate(facebook(), clientContext);
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,
            MediaType.valueOf("text/javascript")));
    template.setMessageConverters(Arrays.<HttpMessageConverter<?>> asList(converter));
    return template;
}

然后在使用中:

public String photos(Model model) throws Exception {
        ObjectNode result = facebookRestTemplate
                .getForObject("https://graph.facebook.com/me/friends", ObjectNode.class);
        ArrayNode data = (ArrayNode) result.get("data");
        ArrayList<String> friends = new ArrayList<String>();
        for (JsonNode dataNode : data) {
            friends.add(dataNode.get("name").asText());
        }
        model.addAttribute("friends", friends);
        return "facebook";
    }

我从项目中接受了上述对朋友的要求。debug_token定制我展示的上述代码以使用 Spring OAuth 休息模板应该不难。希望这可以帮助 :)

于 2019-05-13T09:20:01.137 回答