spring security实战 2-服务端从Facebook读取用户的联系人( Authorization Code)

How to do it…

  1. 模仿之前的步骤,在Facebook创建一个新项目social- authcode 。
    输入网址地址:http://socialauthcode.test/
    还有授权成功后的,重定向的地址:http://localhost:8080/connect/facebook
  2. 新建一个Spring项目
    https://start.spring.io/ 网址新建一个项目,加入web,thymeleaf模块。
  3. pom.xml文件加入以下依赖:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
      <version>1.5.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>1.5.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <version>1.5.10.RELEASE</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.social</groupId>
      <artifactId>spring-social-config</artifactId>
      <version>1.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.social</groupId>
      <artifactId>spring-social-facebook</artifactId>
      <version>2.0.3.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>1.5.10.RELEASE</version>
    </dependency>
    
  4. 在src/main/resources文件夹创建

friends.html文件:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Friends</title>
</head>
<body>
  <h3>Hello,<span th:text="${facebookProfile.name}">User</span>!</h3>
  <h4>Your friends which also allowed social-authcode:</h4>
  <div th:each="friend:${friends}">
    <b th:text="${friend.id}">[id]</b> -
    <b th:text="${friend.name}">[name]</b>
    <hr/>
  </div>
</body>
</html>

5.新建FriendsController.java ,映射friends.html页面,其中ConnectionRepository存储或重定向与某个OAuth2.0 provider的连接,目前是只展示了Facebook。

//FriendsController.java
@Controller
@RequestMapping("/")
public class FriendsController {
  @Autowired
  private Facebook facebook;
  @Autowired
  private ConnectionRepository connectionRepository;
  //链接上一步的html,
  @GetMapping
  public String friends(Model model) {

  //如果没有获取授权,则跳到申请授权页面
  if (connectionRepository.findPrimaryConnection(Facebook.class)== null) {
    //就返回这个链接
    return "redirect:/connect/facebook";
  }
  //如果已拿到授权,就从facebook获取用户和联系人信息,传给friends.html
  String [] fields = { "id", "email", "name" };
  //获取用户信息
  User userProfile = facebook.fetchObject("me", User.class, fields);
  model.addAttribute("facebookProfile", userProfile);
  //获取联系人信息
  PagedList<Reference> friends =facebook.friendOperations().getFriends();
  model.addAttribute("friends", friends);
  return "friends";
}}

6.上述步骤通过授权码模式,完成了从Facebook获取用户信息和联系人信息的大致过程。但是,我们还需创建一些配置类,为了更好得管理这些配置类,我们新建一个名为facebook的package,这些是我们所需的配置类:
《spring security实战 2-服务端从Facebook读取用户的联系人( Authorization Code)》

7.创建EnhancedFacebookProperties 类,加载client_id,client_secret,还有版本号,并把这些内容写入application.properties

//EnhancedFacebookProperties.java
@Component
@ConfigurationProperties(prefix = "facebook")
public class EnhancedFacebookProperties {
private String appId;
private String appSecret;
private String apiVersion;
// getters and setters omitted for brevity
}
//application.properties
facebook.app-id=1948923582021549
facebook.app-secret=1b4b0f882b185094a903e76a661c7c7c
facebook.api-version=2.9

8.创建CustomFacebookServiceProvider 类,用于创建一个OAuth2Template 实例

//CustomFacebookServiceProvider.java
public class CustomFacebookServiceProvider extends AbstractOAuth2ServiceProvider<Facebook> {
  private String appNamespace;
  private String apiVersion;
  public CustomFacebookServiceProvider(String appId, String appSecret, String apiVersion) {
      super(getOAuth2Template(appId, appSecret, apiVersion));
      this.apiVersion = apiVersion;
  }
  private static OAuth2Template getOAuth2Template(String appId, String appSecret, String apiVersion) {
    String graphApiURL = "https://graph.facebook.com/v" + apiVersion + "/";
    OAuth2Template template = new OAuth2Template(appId, appSecret, "https://www.facebook.com/v" +apiVersion + "/dialog/oauth", graphApiURL + "oauth/access_token");
    template.setUseParametersForClientAuthentication(true);
    return template;
  }
  @Override
  public Facebook getApi(String accessToken) {
  FacebookTemplate template = new FacebookTemplate(accessToken,appNamespace);
  template.setApiVersion(apiVersion);
  return template;
  }
}

9.创建CustomFacebookConnectionFactory 类

//CustomFacebookConnectionFactory 
public class CustomFacebookConnectionFactory extends OAuth2ConnectionFactory<Facebook> {
  public CustomFacebookConnectionFactory(String appId, String appSecret, String apiVersion) {
    super("facebook",new CustomFacebookServiceProvider(appId, appSecret, apiVersion),new FacebookAdapter());
  }
}

10.创建FacebookConfiguration 类:

@Configuration
@EnableSocial
@EnableConfigurationProperties(FacebookProperties.class)
public class FacebookConfiguration extends SocialAutoConfigurerAdapter {
  @Autowired
  private EnhancedFacebookProperties properties;
  @Override
  protected ConnectionFactory<?> createConnectionFactory() {
    return new  CustomFacebookConnectionFactory(this.properties.getAppId(),
    this.properties.getAppSecret(),
    this.properties.getApiVersion());
  }
  //提供Facebook 实例
  @Bean
  @ConditionalOnMissingBean(Facebook.class)
  @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
  public Facebook facebook(ConnectionRepository repository) {
    Connection<Facebook> connection = repository.findPrimaryConnection(Facebook.class);
    return connection != null ? connection.getApi() : null;
  }
  //当我们使用spring social的时候,我们会使用ConnectController 类来处理重定向的问题。默认情况下,spring social会根据request URL ,自动构造redirect URL,因为这个应用程序可能藏在代理下,provider没办法识别url,因此,我们在这里手动输入
  @Bean
  public ConnectController connectController(ConnectionFactoryLocator factoryLocator, ConnectionRepository repository) {
    ConnectController controller = new ConnectController(factoryLocator, repository);
    controller.setApplicationUrl("https://localhost:8080");
    return controller;
  }
}

11.在之前构造的FriendsController,已经完成了几乎所有的OAuth2 验证流程,但是我们还需要编写两个html文件来完成两个页面的加载,这两个页面是我们需要提供{provider}Connect 和{provider}Connected ,在本例中,是Facebook。因而,在templates/connect/文件夹新建2个文件:facebookConnect.html,facebookConnected.html,并填入以下代码:
《spring security实战 2-服务端从Facebook读取用户的联系人( Authorization Code)》

//facebookConnected.html
<html>
<head><title>Social Authcode</title></head>
<body>
  <h2>Connected to Facebook</h2>
  <p>Click <a href="/">here</a> to see your friends.</p>
</body>
</html>


//facebookConnect.html
<html>
<head>
<title>Social Authcode</title>
</head>
<body>
  <h2>Connect to Facebook to see your contacts</h2>
  <form action="/connect/facebook" method="POST">
    <input type="hidden" name="scope" value="public_profile user_friends" />
    <input type="hidden" name="response_type" value="code" />
    <div class="formInfo">
      Click the button to share your contacts with <b>social-authcode</b>
    </div>
    <p><button type="submit">Connect to Facebook</button></p>
  </form>
</body>
</html>

How it works…

这一章展示了,怎样去注册应用,怎样通过Authorization Code type(授权码) 模式,来连接facebook.由于是server端的连接,比起client端的连接方式(implict grant type)更加安全。我们使用Spring Social来完成social-authcode和Facebook的会话。Spring Social提供ConnectController类来开启会话流程,处理返回数据,因而,我们也要提供页面来接收这些数据。为了

There’s more…

当我们在Facebook注册了应用后,将redirect url配置为http://localhost:8080/connect,为什么不使用http://localhost:8080/callback?因为ConnectController可以处理/connect的endpoint,如果你不想使用Spring Social,你将要自己来验证授权码.

尽管,我们使用了Spring Social Facebook,我们依然创建了一些以“Custom”开头的类。通过这些类,我们可以定制化OAuth2Template和FacebookTemplate 实例的构建,这很重要,因为当前的FacebookTemplate 版本,可以支持这样的定制。

此外,在客户端和OAuth 2.0 Provider 的交互过程中,我们没有使用TLS/SSL,注册地址我们使用的是HTTP而非HTTPS,书中的例子是为了简单展示,才简化过程。但是在生产环境中,请使用HTTPs来保护数据。

另一件有价值的事,是使用关系数据库管理系统,来持久化你与Provider的连接,我们之前将连接保存在内存中,这意味着,一旦我们重启服务,连接就会丢失。如果你想使用数据库,那么你可能需要定义一个bean“JdbcUsersConnectionRepository ”,并且在数据库,通过如下命令构建表:

create table UserConnection (userId varchar(255) not null,
providerId varchar(255) not null,
providerUserId varchar(255),
rank int not null,
displayName varchar(255),
profileUrl varchar(512),
imageUrl varchar(512),
accessToken varchar(512) not null,
secret varchar(512),
refreshToken varchar(512),
expireTime bigint,
primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId,providerId, rank);
    原文作者:Lavender
    原文地址: https://segmentfault.com/a/1190000015281172
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞