spring-boot - 如何使用 Spring(java) 验证 facebook 授权 token 并注册用户

标签 spring-boot spring-security-oauth2 spring-oauth2 spring-security-rest

我正在开发一个应用程序,其前端使用 React.js 编写,后端 REST API 使用 Spring 框架编写。我想在我的网站上添加社交登录,所以经过几天的谷歌搜索和研究,我明白 OAuth2 是解决方案。我开始知道前端应该处理从资源服务器(此处为 Facebook)获取授权 token ,而我的后端(java)应该验证该 token 并与 Facebook 连接以获取访问 token 。然后该访问 token 应与用户详细信息(例如电子邮件)一起存储在我的数据库中。

这是我的要求,一旦用户点击“继续使用 Facebook” 按钮,我的应用程序应该使用详细信息 - 电子邮件和姓名(注册功能)在我自己的数据库中创建帐户。以后每当他们再次单击此按钮时,他们将登录而不注册。其他网站的处理方式。

截至目前,我的应用程序中的按钮正在运行,它为我带来了来自 Facebook 的授权 token 。

有人可以指导我在这里应该遵循的路径吗?

另外,我应该特别注意一些错误处理。

最佳答案

以下是使用 Spring Boot 作为 REST API 的一般方法,该 API 由 Spring Data JPA 和 Spring Security 支持,适用于 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获取用户访问 token 和用户的 Facebook 用户 ID。在成功的情况下,你会在你的 react 应用中收到来自 facebook 的回复,如下所示:
{
    status: 'connected',
    authResponse: {
        accessToken: '...',
        expiresIn:'...',
        reauthorize_required_in:'...'
        signedRequest:'...',
        userID:'...'
    }
}
  1. 使用在第 2 步中收到的信息(如 /login/facebook)点击某个登录端点。我无法预测您的应用程序的结构。在我的应用程序中,此代码由实现 GenericFilterBean 的身份验证过滤器处理。我用 token 传递了一个 header X-Auth-Facebook

  2. 验证 token 。我在 Authentication authenticate(Authentication authentication) throws AuthenticationException 方法中实现 AuthenticationProvider 的类中执行此操作。此类将需要您的应用的访问 token accessToken 和用户的 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

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

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

编辑:如果你想使用 Spring 0Auth,你可以关注 example用于创建 Spring 2 Oauth Rest 模板

@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";
    }

我接受了来自项目的 friend 的上述请求。定制我用 debug_token 展示的上述代码以使用 Spring OAuth 休息模板应该不难。希望这会有所帮助:)

关于spring-boot - 如何使用 Spring(java) 验证 facebook 授权 token 并注册用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53870860/

相关文章:

java - Spring Data 不记录 findById

spring-security - 在 Spring Weblux 中禁用给定路径的身份验证和 csrf?

java - 使用 OAuth2RestTemplate 请求 HTTPS 资源

java - 如何使用 Spring Security Oauth 忽略某些端点

java - 如何在Spring OAuth2中仅通过client_id和secret进行授权?

java - Spring security 客户 token 增强器未调用

java - LocalContainerEntityManagerFactoryBean

java - spring boot中如何使用自定义logger记录访问日志

java - 通过 Java 以编程方式设置 "hbase.server.keyvalue.maxsize"

java - Spring security oauth2 修改响应主体连接