UPDATE OAuth2 user service to handle user creation and redirect after login
This commit is contained in:
48
frontend/package-lock.json
generated
48
frontend/package-lock.json
generated
@@ -10,7 +10,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.7",
|
"@tailwindcss/vite": "^4.1.7",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
|
"path": "^0.12.7",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-range-slider-input": "^3.2.1",
|
"react-range-slider-input": "^3.2.1",
|
||||||
@@ -2242,6 +2244,18 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||||
|
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.159",
|
"version": "1.5.159",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.159.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.159.tgz",
|
||||||
@@ -2738,6 +2752,12 @@
|
|||||||
"node": ">=0.8.19"
|
"node": ">=0.8.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/is-extglob": {
|
"node_modules/is-extglob": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
@@ -3332,6 +3352,16 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path": {
|
||||||
|
"version": "0.12.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
|
||||||
|
"integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"process": "^0.11.1",
|
||||||
|
"util": "^0.10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-exists": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
@@ -3409,6 +3439,15 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
@@ -3892,6 +3931,15 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util": {
|
||||||
|
"version": "0.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||||
|
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.3.5",
|
"version": "6.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.7",
|
"@tailwindcss/vite": "^4.1.7",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
|
"path": "^0.12.7",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-range-slider-input": "^3.2.1",
|
"react-range-slider-input": "^3.2.1",
|
||||||
|
|||||||
@@ -9,13 +9,18 @@ type props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Topbar = ({sidebarToggled, setSidebarToggled, className}: props) => {
|
const Topbar = ({sidebarToggled, setSidebarToggled, className}: props) => {
|
||||||
|
const apiUrl = import.meta.env.VITE_API_URL;
|
||||||
|
const redirectUri = encodeURIComponent(window.location.href);
|
||||||
|
const loginUrl = `${apiUrl}/oauth2/authorization/google?redirect_uri=${redirectUri}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx(className, "flex justify-between")}>
|
<div className={clsx(className, "flex justify-between")}>
|
||||||
<MenuButton onClick={() => setSidebarToggled(!sidebarToggled)}>
|
<MenuButton onClick={() => setSidebarToggled(!sidebarToggled)}>
|
||||||
{sidebarToggled ? <Menu size={24}/> : <X size={24}/>}
|
{sidebarToggled ? <Menu size={24}/> : <X size={24}/>}
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
|
|
||||||
<MenuButton className={"w-20 text-gray-600"}>
|
<MenuButton className={"w-20 text-gray-600"}
|
||||||
|
onClick={() => globalThis.location.href = loginUrl}>
|
||||||
Login
|
Login
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,15 @@ import tailwindcss from '@tailwindcss/vite'
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), tailwindcss()],
|
plugins: [react(), tailwindcss()],
|
||||||
|
preview: {
|
||||||
|
port: 5173,
|
||||||
|
strictPort: true,
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
|
port: 5173,
|
||||||
|
strictPort: true,
|
||||||
|
host: true,
|
||||||
|
origin: "http://0.0.0.0:5173",
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api/v1': {
|
'/api/v1': {
|
||||||
target: 'http://localhost:8080',
|
target: 'http://localhost:8080',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.ddf.vodsystem.security;
|
package com.ddf.vodsystem.configuration;
|
||||||
import com.ddf.vodsystem.entities.User;
|
import com.ddf.vodsystem.entities.User;
|
||||||
import com.ddf.vodsystem.repositories.UserRepository;
|
import com.ddf.vodsystem.repositories.UserRepository;
|
||||||
|
|
||||||
@@ -8,6 +8,9 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
|||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
||||||
@@ -26,15 +29,18 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
|||||||
String name = oAuth2User.getAttribute("name");
|
String name = oAuth2User.getAttribute("name");
|
||||||
String googleId = oAuth2User.getAttribute("sub");
|
String googleId = oAuth2User.getAttribute("sub");
|
||||||
|
|
||||||
userRepository.findByGoogleId(googleId).orElseGet(() -> {
|
Optional<User> existingUser = userRepository.findByGoogleId(googleId);
|
||||||
|
|
||||||
|
if (existingUser.isEmpty()) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
user.setName(name);
|
user.setName(name);
|
||||||
user.setGoogleId(googleId);
|
user.setGoogleId(googleId);
|
||||||
user.setUsername(email);
|
user.setUsername(email);
|
||||||
user.setRole(0);
|
user.setRole(0);
|
||||||
return userRepository.save(user);
|
user.setCreatedAt(LocalDateTime.now());
|
||||||
});
|
userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
return oAuth2User;
|
return oAuth2User;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ddf.vodsystem.security;
|
package com.ddf.vodsystem.configuration;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
@@ -7,7 +8,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
|||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@@ -15,6 +15,9 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
private final CustomOAuth2UserService customOAuth2UserService;
|
private final CustomOAuth2UserService customOAuth2UserService;
|
||||||
|
|
||||||
|
@Value("${frontend.url}")
|
||||||
|
private String frontendUrl;
|
||||||
|
|
||||||
public SecurityConfig(CustomOAuth2UserService customOAuth2UserService) {
|
public SecurityConfig(CustomOAuth2UserService customOAuth2UserService) {
|
||||||
this.customOAuth2UserService = customOAuth2UserService;
|
this.customOAuth2UserService = customOAuth2UserService;
|
||||||
}
|
}
|
||||||
@@ -24,6 +27,7 @@ public class SecurityConfig {
|
|||||||
http
|
http
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/api/v1/auth/login", "/api/v1/auth/user").permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.oauth2Login(oauth2 -> oauth2
|
.oauth2Login(oauth2 -> oauth2
|
||||||
@@ -39,7 +43,7 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuthenticationSuccessHandler successHandler() {
|
public AuthenticationSuccessHandler successHandler() {
|
||||||
return new SimpleUrlAuthenticationSuccessHandler("/api/v1/auth/user");
|
return (request, response, authentication) -> response.sendRedirect(frontendUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,9 +16,4 @@ public class AuthController {
|
|||||||
public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
|
public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
|
||||||
return principal.getAttributes();
|
return principal.getAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/login")
|
|
||||||
public String login() {
|
|
||||||
return "login";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,3 +13,6 @@ spring.sql.init.data-locations=classpath:db/data.sql
|
|||||||
spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID}
|
spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID}
|
||||||
spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET}
|
spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET}
|
||||||
spring.security.oauth2.client.registration.google.scope=profile,email
|
spring.security.oauth2.client.registration.google.scope=profile,email
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
frontend.url=http://localhost:5173
|
||||||
|
|||||||
Reference in New Issue
Block a user