Skip to content

Commit 421593d

Browse files
committed
Polish code for ch9
1 parent d1ca2e4 commit 421593d

37 files changed

Lines changed: 726 additions & 426 deletions

8/part3/chat/src/main/java/com/greglturnquist/learningspringboot/chat/WebSocketConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ HandlerMapping webSocketMapping(CommentService commentService,
4343
urlMap.put("/app/chatMessage.new", inboundChatService);
4444
urlMap.put("/topic/chatMessage.new", outboundChatService);
4545

46+
// tag::cors[]
4647
Map<String, CorsConfiguration> corsConfigurationMap =
4748
new HashMap<>();
4849
CorsConfiguration corsConfiguration = new CorsConfiguration();
@@ -53,6 +54,7 @@ HandlerMapping webSocketMapping(CommentService commentService,
5354
"/app/chatMessage.new", corsConfiguration);
5455
corsConfigurationMap.put(
5556
"/topic/chatMessage.new", corsConfiguration);
57+
// end::cors[]
5658

5759
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
5860
mapping.setOrder(10);

9/part1/chat/build.gradle

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ buildscript {
22
ext {
33
springBootVersion = '2.0.0.M3'
44
springCloudVersion = 'Finchley.BUILD-SNAPSHOT'
5+
springCloudStreamVersion = 'Elmhurst.M1'
56
}
67
repositories {
78
mavenCentral()
@@ -28,11 +29,13 @@ sourceCompatibility = 1.8
2829
targetCompatibility = 1.8
2930

3031
repositories {
32+
mavenLocal()
3133
mavenCentral()
3234
maven { url "https://repo.spring.io/snapshot" }
3335
maven { url "https://repo.spring.io/milestone" }
3436
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
3537
maven { url "https://dl.bintray.com/rabbitmq/maven-milestones/" }
38+
mavenLocal()
3639
}
3740

3841
jar {
@@ -45,12 +48,11 @@ configurations {
4548
all*.exclude group: 'org.springframework', module: 'spring-webmvc'
4649
}
4750

48-
//ext['spring.version'] = '5.0.0.BUILD-SNAPSHOT'
49-
//ext['spring-security.version'] = '5.0.0.BUILD-SNAPSHOT'
5051
ext['spring-session.version'] = '2.0.0.BUILD-SNAPSHOT'
5152

5253
dependencies {
5354
compile('org.springframework.boot:spring-boot-starter-webflux')
55+
compile('org.synchronoss.cloud:nio-multipart-parser:1.1.0')
5456
compile('org.springframework.boot:spring-boot-starter-actuator')
5557
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
5658
compile('org.springframework.boot:spring-boot-devtools')
@@ -73,7 +75,6 @@ dependencies {
7375
// end::zuul[]
7476

7577
// tag::thymeleaf-security[]
76-
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4:2.1.3.RELEASE')
7778
// end::thymeleaf-security[]
7879

7980
testCompile('org.springframework.boot:spring-boot-starter-test')
@@ -82,5 +83,6 @@ dependencies {
8283
dependencyManagement {
8384
imports {
8485
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
86+
mavenBom "org.springframework.cloud:spring-cloud-stream-dependencies:${springCloudStreamVersion}"
8587
}
8688
}

9/part1/chat/src/main/java/com/greglturnquist/learningspringboot/chat/UserParsingHandshakeHandler.java renamed to 9/part1/chat/src/main/java/com/greglturnquist/learningspringboot/chat/AuthorizedWebSocketHandler.java

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,48 +15,34 @@
1515
*/
1616
package com.greglturnquist.learningspringboot.chat;
1717

18-
import java.util.HashMap;
19-
import java.util.Map;
20-
import java.util.stream.Stream;
18+
import java.security.Principal;
2119

2220
import reactor.core.publisher.Mono;
23-
21+
import org.springframework.security.core.Authentication;
2422
import org.springframework.web.reactive.socket.WebSocketHandler;
2523
import org.springframework.web.reactive.socket.WebSocketSession;
2624

2725
/**
2826
* @author Greg Turnquist
2927
*/
3028
// tag::code[]
31-
abstract class UserParsingHandshakeHandler
29+
abstract class AuthorizedWebSocketHandler
3230
implements WebSocketHandler {
3331

34-
private final Map<String, String> userMap;
35-
36-
UserParsingHandshakeHandler() {
37-
this.userMap = new HashMap<>();
38-
}
39-
4032
@Override
4133
public final Mono<Void> handle(WebSocketSession session) {
42-
43-
this.userMap.put(session.getId(),
44-
Stream.of(session.getHandshakeInfo().getUri()
45-
.getQuery().split("&"))
46-
.map(s -> s.split("="))
47-
.filter(strings -> strings[0].equals("user"))
48-
.findFirst()
49-
.map(strings -> strings[1])
50-
.orElse(""));
51-
52-
return handleInternal(session);
34+
return session.getHandshakeInfo().getPrincipal()
35+
.filter(this::isAuthorized)
36+
.then(doHandle(session));
5337
}
5438

55-
abstract protected Mono<Void> handleInternal(
56-
WebSocketSession session);
57-
58-
String getUser(String id) {
59-
return userMap.get(id);
39+
private boolean isAuthorized(Principal principal) {
40+
Authentication authentication = (Authentication) principal;
41+
return authentication.isAuthenticated() &&
42+
authentication.getAuthorities().contains("ROLE_USER");
6043
}
44+
45+
abstract protected Mono<Void> doHandle(
46+
WebSocketSession session);
6147
}
6248
// end::code[]

9/part1/chat/src/main/java/com/greglturnquist/learningspringboot/chat/CommentService.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@
2020
import reactor.core.publisher.Flux;
2121
import reactor.core.publisher.FluxSink;
2222
import reactor.core.publisher.Mono;
23-
2423
import org.springframework.cloud.stream.annotation.EnableBinding;
2524
import org.springframework.cloud.stream.annotation.StreamListener;
2625
import org.springframework.stereotype.Service;
27-
import org.springframework.web.reactive.socket.WebSocketHandler;
2826
import org.springframework.web.reactive.socket.WebSocketSession;
2927

3028
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -36,7 +34,7 @@
3634
// tag::code[]
3735
@Service
3836
@EnableBinding(ChatServiceStreams.class)
39-
public class CommentService implements WebSocketHandler {
37+
public class CommentService extends AuthorizedWebSocketHandler {
4038

4139
private final static Logger log =
4240
LoggerFactory.getLogger(CommentService.class);
@@ -64,7 +62,7 @@ public void broadcast(Comment comment) {
6462
}
6563

6664
@Override
67-
public Mono<Void> handle(WebSocketSession session) {
65+
public Mono<Void> doHandle(WebSocketSession session) {
6866
return session.send(this.flux
6967
.map(comment -> {
7068
try {
@@ -78,6 +76,5 @@ public Mono<Void> handle(WebSocketSession session) {
7876
.log("wrap-as-websocket-message"))
7977
.log("publish-to-websocket");
8078
}
81-
8279
}
8380
// end::code[]

9/part1/chat/src/main/java/com/greglturnquist/learningspringboot/chat/GatewayConfig.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,33 @@
3131
@Configuration
3232
public class GatewayConfig {
3333

34-
private static final Logger log = LoggerFactory.getLogger(GatewayConfig.class);
35-
36-
@Bean
37-
SaveSessionWebFilterFactory saveSessionWebFilterFactory() {
38-
return new SaveSessionWebFilterFactory();
39-
}
34+
private static final Logger log =
35+
LoggerFactory.getLogger(GatewayConfig.class);
4036

4137
/**
4238
* Force the current WebSession to get saved
4339
*/
44-
static class SaveSessionWebFilterFactory implements WebFilterFactory {
40+
static class SaveSessionWebFilterFactory
41+
implements WebFilterFactory {
4542
@Override
4643
public WebFilter apply(Tuple args) {
4744
return (exchange, chain) -> exchange.getSession()
4845
.map(webSession -> {
4946
log.debug("Session id: " + webSession.getId());
50-
webSession.getAttributes().entrySet().forEach(entry ->
51-
log.debug(entry.getKey() + " => " + entry.getValue()));
47+
webSession.getAttributes().entrySet()
48+
.forEach(entry ->
49+
log.debug(entry.getKey() + " => " +
50+
entry.getValue()));
5251
return webSession;
5352
})
5453
.map(WebSession::save)
5554
.then(chain.filter(exchange));
5655
}
5756
}
57+
58+
@Bean
59+
SaveSessionWebFilterFactory saveSessionWebFilterFactory() {
60+
return new SaveSessionWebFilterFactory();
61+
}
5862
}
5963
// end::code[]

9/part1/chat/src/main/java/com/greglturnquist/learningspringboot/chat/HomeController.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
*/
1616
package com.greglturnquist.learningspringboot.chat;
1717

18+
import org.springframework.security.core.Authentication;
19+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1820
import org.springframework.stereotype.Controller;
21+
import org.springframework.ui.Model;
1922
import org.springframework.web.bind.annotation.GetMapping;
2023

2124
/**
@@ -24,9 +27,12 @@
2427
@Controller
2528
public class HomeController {
2629

30+
// tag::code[]
2731
@GetMapping("/")
28-
public String index() {
32+
public String index(@AuthenticationPrincipal Authentication auth, Model model) {
33+
model.addAttribute("authentication", auth);
2934
return "index";
3035
}
36+
// end::code[]
3137

3238
}

9/part1/chat/src/main/java/com/greglturnquist/learningspringboot/chat/InboundChatService.java

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package com.greglturnquist.learningspringboot.chat;
1717

1818
import reactor.core.publisher.Mono;
19-
2019
import org.springframework.cloud.stream.annotation.EnableBinding;
2120
import org.springframework.messaging.support.MessageBuilder;
2221
import org.springframework.stereotype.Service;
@@ -29,7 +28,7 @@
2928
// tag::code[]
3029
@Service
3130
@EnableBinding(ChatServiceStreams.class)
32-
public class InboundChatService extends UserParsingHandshakeHandler {
31+
public class InboundChatService extends AuthorizedWebSocketHandler {
3332

3433
private final ChatServiceStreams chatServiceStreams;
3534

@@ -38,30 +37,28 @@ public InboundChatService(ChatServiceStreams chatServiceStreams){
3837
}
3938

4039
@Override
41-
protected Mono<Void> handleInternal(WebSocketSession session) {
42-
return session
43-
.receive()
44-
.log(getUser(session.getId())
40+
protected Mono<Void> doHandle(WebSocketSession session) {
41+
//end::code[]
42+
return session.receive()
43+
.log(session.getId()
4544
+ "-inbound-incoming-chat-message")
4645
.map(WebSocketMessage::getPayloadAsText)
47-
.log(getUser(session.getId())
46+
.log(session.getId()
4847
+ "-inbound-convert-to-text")
49-
.flatMap(message ->
50-
broadcast(message, getUser(session.getId())))
51-
.log(getUser(session.getId())
48+
.flatMap(message -> broadcast(message, session))
49+
.log(session.getId()
5250
+ "-inbound-broadcast-to-broker")
5351
.then();
5452
}
5553

56-
public Mono<?> broadcast(String message, String user) {
57-
return Mono.fromRunnable(() -> {
58-
chatServiceStreams.clientToBroker().send(
54+
Mono<?> broadcast(String message, WebSocketSession user) {
55+
return user.getHandshakeInfo().getPrincipal()
56+
.map(principal -> chatServiceStreams.clientToBroker().send(
5957
MessageBuilder
6058
.withPayload(message)
61-
.setHeader(ChatServiceStreams.USER_HEADER, user)
62-
.build());
63-
});
59+
.setHeader(ChatServiceStreams.USER_HEADER,
60+
principal.getName())
61+
.build()));
6462
}
6563

6664
}
67-
// end::code[]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.greglturnquist.learningspringboot.chat;
17+
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
import org.springframework.boot.CommandLineRunner;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.data.mongodb.core.MongoOperations;
24+
25+
/**
26+
* @author Greg Turnquist
27+
*/
28+
// tag::code[]
29+
@Configuration
30+
public class InitUsers {
31+
32+
@Bean
33+
CommandLineRunner initializeUsers(MongoOperations operations) {
34+
return args -> {
35+
operations.dropCollection(User.class);
36+
37+
operations.insert(
38+
new User(
39+
null,
40+
"greg", "turnquist",
41+
new String[]{"ROLE_USER", "ROLE_ADMIN"}));
42+
operations.insert(
43+
new User(
44+
null,
45+
"phil", "webb",
46+
new String[]{"ROLE_USER"}));
47+
48+
operations.findAll(User.class).forEach(user -> {
49+
System.out.println("Loaded " + user);
50+
});
51+
};
52+
}
53+
}
54+
// end::code[]

0 commit comments

Comments
 (0)