Skip to content

Commit ebe6235

Browse files
authored
Merge pull request #2 from Magmusacy/messages-auth
Messages auth
2 parents 45aa9e8 + 5435bb8 commit ebe6235

17 files changed

Lines changed: 146 additions & 52 deletions

File tree

backend/chatapp/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ dependencies {
4040
implementation 'io.jsonwebtoken:jjwt-api:0.13.0'
4141
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.13.0'
4242
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.13.0'
43-
43+
implementation 'org.springframework.security:spring-security-messaging'
4444
// Testcontainers dependencies
4545
testImplementation 'org.testcontainers:testcontainers:1.19.7'
4646
testImplementation 'org.testcontainers:postgresql:1.19.7'

backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/auth/AuthController.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import com.magmusacy.chat.chatapp.auth.dto.RegisterRequest;
66
import lombok.RequiredArgsConstructor;
77
import org.springframework.http.ResponseEntity;
8-
import org.springframework.stereotype.Controller;
98
import org.springframework.web.bind.annotation.PostMapping;
109
import org.springframework.web.bind.annotation.RequestBody;
1110
import org.springframework.web.bind.annotation.RequestMapping;

backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/auth/AuthService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,28 @@
77
import com.magmusacy.chat.chatapp.user.User;
88
import com.magmusacy.chat.chatapp.user.UserService;
99
import lombok.RequiredArgsConstructor;
10+
import org.springframework.messaging.simp.SimpMessagingTemplate;
1011
import org.springframework.security.authentication.AuthenticationManager;
1112
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1213
import org.springframework.security.crypto.password.PasswordEncoder;
1314
import org.springframework.stereotype.Service;
1415

16+
17+
//TODO: Should this be transactional, like the message should be sent if the transaction goes through
18+
//TODO it might be better to just send the added user but whatever for now that works well
1519
@Service
1620
@RequiredArgsConstructor
1721
public class AuthService {
1822
private final UserService userService;
1923
private final JwtService jwtService;
2024
private final AuthenticationManager authenticationManager;
2125
private final PasswordEncoder passwordEncoder;
26+
private final SimpMessagingTemplate messagingTemplate;
2227

2328
public AuthenticationResponse register(RegisterRequest request) {
2429
User user = userService.createUser(request, passwordEncoder);
2530
String jwtToken = jwtService.generateToken(user);
31+
messagingTemplate.convertAndSend("/topic/users", userService.findAllUsers());
2632
return new AuthenticationResponse((jwtToken));
2733
}
2834

@@ -32,7 +38,9 @@ public AuthenticationResponse login(LoginRequest request) {
3238
);
3339

3440
User user = userService.findByEmail(request.email());
41+
user.setIsOnline(true);
3542
String jwtToken = jwtService.generateToken(user);
43+
messagingTemplate.convertAndSend("/topic/users", userService.findAllUsers());
3644
return new AuthenticationResponse((jwtToken));
3745
}
3846
}

backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/auth/JwtService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public String generateToken(UserDetails userDetails) {
3131
return Jwts.builder()
3232
.setSubject(userDetails.getUsername())
3333
.setIssuedAt(new Date(System.currentTimeMillis()))
34-
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24))
34+
//TODO change expiration behaviour
35+
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 240000))
3536
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
3637
.compact();
3738
}

backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/config/WebSocketAuthenticationInterceptor.java renamed to backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/config/JwtInterceptor.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.magmusacy.chat.chatapp.config;
22

33
import com.magmusacy.chat.chatapp.auth.JwtService;
4+
import com.magmusacy.chat.chatapp.user.User;
5+
import com.magmusacy.chat.chatapp.user.UserService;
46
import lombok.RequiredArgsConstructor;
57
import org.springframework.messaging.Message;
68
import org.springframework.messaging.MessageChannel;
@@ -15,16 +17,18 @@
1517
import org.springframework.stereotype.Component;
1618
import org.springframework.util.StringUtils;
1719

20+
import java.security.Principal;
21+
1822
@Component
1923
@RequiredArgsConstructor
20-
public class WebSocketAuthenticationInterceptor implements ChannelInterceptor {
24+
public class JwtInterceptor implements ChannelInterceptor {
2125
private final JwtService jwtService;
2226
private final UserDetailsService userDetailsService;
27+
private final UserService userService;
2328

2429
@Override
2530
public Message<?> preSend(Message<?> message, MessageChannel channel) {
2631
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
27-
2832
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
2933
String authorizationHeader = accessor.getFirstNativeHeader("Authorization");
3034
if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith("Bearer ")) {
@@ -37,10 +41,24 @@ public Message<?> preSend(Message<?> message, MessageChannel channel) {
3741
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
3842
userDetails, null, userDetails.getAuthorities()
3943
);
44+
45+
User user = userService.findByEmail(username);
46+
userService.handleUserLogin(user);
47+
48+
// This could be authToken.getPrincipal() ???
4049
accessor.setUser(authToken);
41-
SecurityContextHolder.getContext().setAuthentication(authToken);
4250
}
4351
}
52+
} else {
53+
throw new IllegalStateException("No JWT token supplied.");
54+
}
55+
}
56+
57+
if (StompCommand.DISCONNECT.equals(accessor.getCommand())) {
58+
Principal potentialUser = accessor.getUser();
59+
if (potentialUser != null) {
60+
User user = userService.findByEmail(potentialUser.getName());
61+
userService.handleUserLogout(user);
4462
}
4563
}
4664

backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/config/SecurityConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
1010
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1111
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
12+
import org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity;
1213
import org.springframework.security.config.http.SessionCreationPolicy;
1314
import org.springframework.security.core.userdetails.UserDetailsService;
1415
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1516
import org.springframework.security.crypto.password.PasswordEncoder;
1617
import org.springframework.security.web.SecurityFilterChain;
1718
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
19+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
1820

1921
@Configuration
2022
@EnableWebSecurity
@@ -39,7 +41,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
3941
http
4042
.csrf(csrf -> csrf.disable())
4143
.authorizeHttpRequests(auth -> auth
42-
.requestMatchers("/auth/**").permitAll()
44+
.requestMatchers("/auth/**", "/ws/**").permitAll()
4345
.anyRequest().authenticated()
4446
)
4547
.exceptionHandling(exception -> exception

backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/config/WebSocketConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,19 @@
2020
@EnableWebSocketMessageBroker
2121
@RequiredArgsConstructor
2222
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
23-
private final WebSocketAuthenticationInterceptor webSocketAuthenticationInterceptor;
23+
private final JwtInterceptor webSocketAuthenticationInterceptor;
2424

2525
@Override
2626
public void configureMessageBroker(MessageBrokerRegistry config) {
27-
config.enableSimpleBroker("/user");
27+
config.enableSimpleBroker("/user", "/topic");
2828
config.setApplicationDestinationPrefixes("/app");
2929
config.setUserDestinationPrefix("/user");
3030
}
3131

3232
@Override
3333
public void registerStompEndpoints(StompEndpointRegistry registry) {
3434
registry.addEndpoint("/ws")
35+
.setAllowedOriginPatterns("*")
3536
.withSockJS();
3637
}
3738

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.magmusacy.chat.chatapp.user;
2+
3+
import com.magmusacy.chat.chatapp.auth.dto.RegisterRequest;
4+
import org.springframework.boot.CommandLineRunner;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.context.annotation.Profile;
8+
import org.springframework.security.crypto.password.PasswordEncoder;
9+
10+
@Configuration
11+
@Profile("dev")
12+
public class DummyUserInitializer {
13+
14+
@Bean
15+
public CommandLineRunner initDummyUsers(UserService userService, PasswordEncoder passwordEncoder) {
16+
return args -> {
17+
userService.createUser(
18+
new RegisterRequest("Dummy One", "dummy1@example.com", "password", "password"),
19+
passwordEncoder
20+
);
21+
userService.createUser(
22+
new RegisterRequest("Dummy Two", "dummy2@example.com", "password", "password"),
23+
passwordEncoder
24+
);
25+
};
26+
}
27+
}

backend/chatapp/src/main/java/com/magmusacy/chat/chatapp/user/UserController.java

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,14 @@
99
import org.springframework.stereotype.Controller;
1010
import org.springframework.web.bind.annotation.GetMapping;
1111

12+
import java.util.List;
13+
1214
@Controller
1315
@RequiredArgsConstructor
1416
public class UserController {
1517
private final UserService userService;
1618
private final ServerProperties serverProperties;
1719

18-
@MessageMapping("/user.addUser")
19-
@SendTo("/user/topic")
20-
public User addUser(@Payload User user) {
21-
userService.saveUser(user);
22-
return user;
23-
}
24-
25-
@MessageMapping("/user.disconnectUser")
26-
@SendTo("/user/topic")
27-
public User disconnect(
28-
@Payload User user
29-
) {
30-
userService.disconnectUser(user);
31-
return user;
32-
}
33-
34-
// @GetMapping("/users")
35-
// public ResponseEntity<List<User>> findConnectedUsers() {
36-
// return ResponseEntity.ok(userService.findConnectedUsers());
37-
// }
38-
3920
@GetMapping("/user/me")
4021
public ResponseEntity<UserMeDTO> getUserInfo() {
4122
return ResponseEntity.ok(userService.getUserInfo());
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.magmusacy.chat.chatapp.user;
2+
3+
import java.time.LocalDateTime;
4+
5+
public record UserDTO(int id, String name, Boolean isOnline, LocalDateTime lastSeen) {
6+
}

0 commit comments

Comments
 (0)