]> juplo.de Git - website/blob
a351be8f1f8e0649c5a0010a4e4454e1fc27daa5
[website] /
1 ---
2 _edit_last: "2"
3 categories:
4   - howto
5 date: "2016-01-25T13:43:26+00:00"
6 guid: http://juplo.de/?p=613
7 parent_post_id: null
8 post_id: "613"
9 tags:
10   - createmedia.nrw
11   - facebook
12   - graph-api
13   - java
14   - oauth2
15   - spring
16   - spring-boot
17   - spring-social
18 title: 'Develop a Facebook-App with Spring-Social – Part III: Implementing a UserIdSource'
19 url: /develop-a-facebook-app-with-spring-social-part-03-implementing-a-user-id-source/
20
21 ---
22 In this series of Mini-How-Tow's I will describe how to develop a facebook app with the help of [Spring-Social](http://projects.spring.io/spring-social/ "Learn more about Spring-Social")
23
24 In [the last part of this series](/develop-a-facebook-app-with-spring-social-part-02-how-spring-social-works/ "Read part 2 of this series, to understand, why the first example cannot work as a real app!"), I explained, why the nice little example from the Getting-Started-Guide " [Accessing Facebook Data](http://spring.io/guides/gs/accessing-facebook/ "Read the official Getting-Started-Guide")" cannot function as a real facebook-app.
25
26 In this part, we will try to solve that problem, by implementing a `UserIdSource`, that tells Spring Social, which user it should connect to the API.
27
28 ## The Source is With You
29
30 You can find the source-code on [/git/examples/facebook-app/](/git/examples/facebook-app/ "Link for cloning")
31 and [browse it via gitweb](/gitweb/?p=examples/facebook-app;a=summary "Browse the source-code now").
32 Check out `part-03` to get the source for this part of the series.
33
34 ## Introducing `UserIdSource`
35
36 The `UserIdSource` is used by Spring Social to ask us, which user it should connect with the social net.
37 Clearly, to answer that question, we must remeber, which user we are currently interested in!
38
39 ## Remember Your Visitors
40
41 In order to remember the current user, we implement a simple mechanism, that stores the ID of the current user in a cookie and retrieves it from there for subsequent calls.
42 This concept was borrowed — again — from [the official code examples](https://github.com/spring-projects/spring-social-samples "Clone the official code examples from GitHub").
43 You can find it for example in the [quickstart-example](https://github.com/spring-projects/spring-social-samples/tree/master/spring-social-quickstart "Clone the quickstart-example from GitHub").
44
45 **It is crucial to stress, that this concept is inherently insecure and should never be used in a production-environment.**
46 As the ID of the user is stored in a cookie, an attacker could simply take over control by sending the ID of any currently connected user, he is interested in.
47
48 The concept is implemented here only for educational purposes.
49 It will be replaced by Spring Security later on.
50 But for the beginning, it is easier to understand, how Spring Social works, if we implement a simple version of the mechanism ourself.
51
52 ## Pluging in Our New Memory
53
54 The internals of our implementation are not of interest.
55 You may explore them by yourself.
56 In short, it stores the ID of each new user in a cookie.
57 By inspecting that cookie, it can restore the ID of the user on subsequent calls.
58
59 What is from interest here is, how we can plug in this simple example-mechanism in Spring Social.
60
61 Mainly, there are two hooks to do that, that means: two interfaces, we have to implement:
62
63 1. **UserIdSource**:
64    Spring Social uses an instance of this interface to ask us, which users authorizations it should load from its persistent store of user/connection-mappings.
65    We already have seen an implementation of that one in [the last part of our series](develop-a-facebook-app-with-spring-social-part-02-how-spring-social-works/#AnonymousUserIdSource "Jump back to the last part of our series").
66
67 1. **ConnectionSignUp**:
68    Spring Social uses an instance of this interface, to ask us about the name it should use for a new user during sign-up.
69
70 ## Implementation
71
72 The implementation of `ConnectionSignUp` simply uses the ID, that is provided by the social network.
73 Since we are only signing in users from Facebook, these ID's are guaranteed to be unique.
74
75 ```Java
76 public class ProviderUserIdConnectionSignUp implements ConnectionSignUp
77 {
78   @Override
79   public String execute(Connection connection)
80   {
81     return connection.getKey().getProviderUserId();
82   }
83 }
84
85 ```
86
87 The implementation of `UserIdSource` retrieves the ID, that was stored in the `SecurityContext` (our simple implementation — not to be confused with the class from Spring Security).
88 If no user is stored in the `SecurityContext`, it falls back to the old behavior and returns the fix id `anonymous`.
89
90 ```Java
91 public class SecurityContextUserIdSource implements UserIdSource
92 {
93   private final static Logger LOG =
94       LoggerFactory.getLogger(SecurityContextUserIdSource.class);
95
96   @Override
97   public String getUserId()
98   {
99     String user = SecurityContext.getCurrentUser();
100     if (user != null)
101     {
102       LOG.debug("found user \"{}\" in the security-context", user);
103     }
104     else
105     {
106       LOG.info("found no user in the security-context, using \"anonymous\"");
107       user = "anonymous";
108     }
109     return user;
110   }
111 }
112
113 ```
114
115 ## Actual Plumbing
116
117 To replace the `AnonymousUserIdSource` by our new implementation, we simply instantiate that instead of the old one in our configuration-class `SocialConfig`:
118
119 ```Java
120 @Override
121 public UserIdSource getUserIdSource()
122 {
123   return new SecurityContextUserIdSource();
124 }
125
126 ```
127
128 There are several ways to plug in the `ConnectionSignUp`.
129 I decided, to plug it into the instance of `InMemoryUsersConnectionRepository`, that our configuration uses, because this way, the user will be signed up automatically on sign in, if it is not known to the application:
130
131 ```Java
132 @Override
133 public UsersConnectionRepository getUsersConnectionRepository(
134     ConnectionFactoryLocator connectionFactoryLocator
135     )
136 {
137   InMemoryUsersConnectionRepository repository =
138       new InMemoryUsersConnectionRepository(connectionFactoryLocator);
139   repository.setConnectionSignUp(new ProviderUserIdConnectionSignUp());
140   return repository;
141 }
142
143 ```
144
145 This makes sense, because our facebook-app uses Facebook, to sign in its users, and, because of that, does not have its own user-model.
146 It can just reuse the user-data provided by facebook.
147
148 The other approach would be, to officially sign up users, that are not known to the app.
149 This is achieved, by redirecting to a special URL, if a sign-in fails, because the user is unknown.
150 These URL then presents a formular for sign-up, which can be prepopulated with the user-data provided by the social network.
151 You can read more about this approach in the [official documentation](http://docs.spring.io/spring-social/docs/1.1.4.RELEASE/reference/htmlsingle/#signing-up-after-a-failed-sign-in "Read more on signing up after a faild sign-in in the official documentation").
152
153 ## Run It!
154
155 So, let us see, if our refinement works. Run the following command and log into your app with at least two different users:
156
157 ```bash
158 git clone /git/examples/facebook-app/
159 cd facebook-app
160 checkout part-00
161 mvn spring-boot:run \
162     -Dfacebook.app.id=YOUR_ID \
163     -Dfacebook.app.secret=YOUR_SECRET \
164     -Dlogging.level.de.juplo.yourshouter=debug
165
166 ```
167
168 (The last part of the command turns on the `DEBUG` logging-level, to see in detail, what is going on.
169
170 ## But What The \*\#! Is Going On There?!?
171
172 **Unfortunately, our application shows exactly the same behavior as, before our last refinement.**
173 Why that?
174
175 If you run the application in a debugger and put a breakpoint in our implementation of `ConnectionSignUp`, you will see, that this code is never called.
176 But it is plugged in in the right place and should be called, if _a new user signs in_!
177
178 The solution is, that we are using the wrong mechanism.
179 We are still using the `ConnectController` which was configured in the simple example, we extended.
180 But this controller is meant to connect a _known user_ to one or more _new social services_.
181 This controller assumes, that the user is already signed in to the application and can be retrieved via the configured `UserIdSource`.
182
183 **To sign in a user to our application, we have to use the `ProviderSignInController` instead!**
184
185 ## Coming next...
186
187 In [the next part](/develop-a-facebook-app-with-spring-social-part-04-signing-in-users "Jump to the next part of this series and read on...") of this series, I will show you, how to change the configuration, so that the `ProviderSignInController` is used to sign in (and automatically sign up) users, that were authenticated through the Graph-API from Facebook.