feature login autho2
This commit is contained in:
parent
e143963a73
commit
e8a74d6edc
@ -11,8 +11,6 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
@ -36,10 +34,4 @@ public class AuthenticationRestController {
|
||||
return ResponseEntity.ok(service.register(request));
|
||||
}
|
||||
|
||||
// Optional: simple endpoint to start OAuth2 authorization if frontend needs a redirect URL
|
||||
@GetMapping("/oauth2/authorization")
|
||||
public ResponseEntity<Void> oauth2Authorization(@RequestParam("provider") String provider) {
|
||||
// Frontend should redirect to /oauth2/authorization/{provider}
|
||||
return ResponseEntity.status(302).header("Location", "/oauth2/authorization/" + provider).build();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package com.dh7789dev.xpeditis.controller.api.v1;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth/oauth2")
|
||||
public class OAuth2Controller {
|
||||
|
||||
@GetMapping("/authorization/{provider}")
|
||||
public void redirectToOAuth2Provider(@PathVariable String provider, HttpServletResponse response) throws IOException {
|
||||
response.sendRedirect("/oauth2/authorization/" + provider);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,11 @@
|
||||
<artifactId>service</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.dh7789dev</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.dh7789dev</groupId>
|
||||
<artifactId>infrastructure</artifactId>
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
package com.dh7789dev.xpeditis.configuration;
|
||||
|
||||
|
||||
import com.dh7789dev.xpeditis.dao.UserDao;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class AuthenticationConfiguration {
|
||||
|
||||
private static final String USER_NOT_FOUND_MSG = "User %s not found";
|
||||
|
||||
private final UserDao userDao;
|
||||
|
||||
public AuthenticationConfiguration(UserDao userDao) {
|
||||
this.userDao = userDao;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService() {
|
||||
return identifier -> userDao.findByUsernameOrEmail(identifier)
|
||||
.orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, identifier)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
final DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
package com.dh7789dev.xpeditis.configuration;
|
||||
|
||||
import com.dh7789dev.xpeditis.dao.UserDao;
|
||||
import com.dh7789dev.xpeditis.entity.UserEntity;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
||||
|
||||
private final UserDao userDao;
|
||||
|
||||
@Override
|
||||
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
|
||||
OAuth2User oAuth2User = super.loadUser(userRequest);
|
||||
|
||||
Map<String, Object> attributes = oAuth2User.getAttributes();
|
||||
String registrationId = userRequest.getClientRegistration().getRegistrationId();
|
||||
|
||||
String email;
|
||||
if ("google".equals(registrationId)) {
|
||||
email = (String) attributes.get("email");
|
||||
} else if ("linkedin".equals(registrationId)) {
|
||||
email = (String) attributes.getOrDefault("email", attributes.get("emailAddress"));
|
||||
} else {
|
||||
throw new OAuth2AuthenticationException("Unsupported provider: " + registrationId);
|
||||
}
|
||||
|
||||
if (email == null) {
|
||||
throw new OAuth2AuthenticationException("Email not provided by OAuth2 provider.");
|
||||
}
|
||||
|
||||
UserEntity user = userDao.findByEmail(email)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("No local user bound to email: " + email));
|
||||
|
||||
return new DefaultOAuth2User(user.getAuthorities(), attributes, "email");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
package com.dh7789dev.xpeditis.configuration;
|
||||
|
||||
import com.dh7789dev.xpeditis.dao.UserDao;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
@ -25,14 +23,6 @@ import java.util.TimeZone;
|
||||
@EnableAsync
|
||||
public class GlobalConfiguration {
|
||||
|
||||
private static final String USER_NOT_FOUND_MSG = "User %s not found";
|
||||
|
||||
private final UserDao userDao;
|
||||
|
||||
public GlobalConfiguration(UserDao userDao) {
|
||||
this.userDao = userDao;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommonsRequestLoggingFilter loggingFilter() {
|
||||
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
|
||||
@ -86,10 +76,4 @@ public class GlobalConfiguration {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService() {
|
||||
return identifier -> userDao.findByUsernameOrEmail(identifier)
|
||||
.orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, identifier)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,51 +1,48 @@
|
||||
package com.dh7789dev.xpeditis.configuration;
|
||||
|
||||
import com.dh7789dev.xpeditis.dao.TokenDao;
|
||||
import com.dh7789dev.xpeditis.dao.UserDao;
|
||||
import com.dh7789dev.xpeditis.entity.TokenEntity;
|
||||
import com.dh7789dev.xpeditis.entity.UserEntity;
|
||||
import com.dh7789dev.xpeditis.util.JwtUtil;
|
||||
import com.dh7789dev.xpeditis.AuthenticationRepository;
|
||||
import com.dh7789dev.xpeditis.CommonUtil;
|
||||
import com.dh7789dev.xpeditis.UserRepository;
|
||||
import com.dh7789dev.xpeditis.dto.app.UserAccount;
|
||||
import com.dh7789dev.xpeditis.dto.request.AuthenticationRequest;
|
||||
import com.dh7789dev.xpeditis.dto.response.AuthenticationResponse;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
// Suppression de @Component - le bean sera créé dans SecurityConfiguration
|
||||
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
private final UserDao userDao;
|
||||
private final TokenDao tokenDao;
|
||||
private final JwtUtil jwtUtil;
|
||||
private final UserRepository userRepository;
|
||||
private final AuthenticationRepository authenticationRepository;
|
||||
|
||||
public OAuth2AuthenticationSuccessHandler(UserRepository userRepository,
|
||||
AuthenticationRepository authenticationRepository) {
|
||||
this.userRepository = userRepository;
|
||||
this.authenticationRepository = authenticationRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
|
||||
String email = authentication.getName();
|
||||
UserEntity user = userDao.findByEmail(email)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found for email: " + email));
|
||||
public void onAuthenticationSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
|
||||
String accessToken = jwtUtil.generateToken(user);
|
||||
String refreshToken = jwtUtil.generateRefreshToken(user);
|
||||
DefaultOAuth2User oAuth2User = (DefaultOAuth2User) authentication.getPrincipal();
|
||||
String email = (String) oAuth2User.getAttributes().get("email");
|
||||
String password = CommonUtil.generatePassword(12);
|
||||
|
||||
// Save token (like classic login)
|
||||
TokenEntity tokenEntity = new TokenEntity()
|
||||
.setUser(user)
|
||||
.setToken(accessToken)
|
||||
.setTokenType(TokenEntity.Type.BEARER)
|
||||
.setExpired(false)
|
||||
.setRevoked(false);
|
||||
tokenDao.save(tokenEntity);
|
||||
UserAccount user = userRepository.findOrCreateOAuthUser(email, oAuth2User.getAttributes(), password);
|
||||
AuthenticationResponse authResponse = authenticationRepository.authenticate(
|
||||
new AuthenticationRequest(user.getEmail(), user.getPassword()));
|
||||
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().write("{\"access_token\":\"" + accessToken + "\", \"refresh_token\":\"" + refreshToken + "\"}");
|
||||
response.getWriter().flush();
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
new ObjectMapper().writeValue(response.getWriter(), authResponse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@ -15,35 +14,33 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
|
||||
import com.dh7789dev.xpeditis.UserRepository;
|
||||
import com.dh7789dev.xpeditis.AuthenticationRepository;
|
||||
|
||||
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
|
||||
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
//@EnableMethodSecurity
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Value("${application.csrf.enabled}")
|
||||
boolean csrfEnabled;
|
||||
|
||||
private static final String ADMIN_ROLE = "ADMIN";
|
||||
// private static final String ADMIN_PLATFORM_ROLE = "ADMIN_PLATFORM";
|
||||
|
||||
private static final String ADMIN_PLATFORM_ROLE = "ADMIN_PLATFORM";
|
||||
private static final String API_V1_URI = "/api/v1/**";
|
||||
|
||||
private static final String[] WHITE_LIST_URL = {
|
||||
"/api/v1/auth/**",
|
||||
"/actuator/health/**",
|
||||
"/oauth2/**",
|
||||
"/login/oauth2/**"};
|
||||
"/actuator/health/**"
|
||||
};
|
||||
|
||||
private static final String[] INTERNAL_WHITE_LIST_URL = {
|
||||
"/v2/api-docs",
|
||||
@ -56,28 +53,26 @@ public class SecurityConfiguration {
|
||||
"/swagger-ui/**",
|
||||
"/webjars/**",
|
||||
"/swagger-ui.html",
|
||||
"/addevent"};
|
||||
"/addevent"
|
||||
};
|
||||
|
||||
private static final WebExpressionAuthorizationManager INTERNAL_ACCESS =
|
||||
new WebExpressionAuthorizationManager("hasIpAddress('127.0.0.1') or hasIpAddress('0:0:0:0:0:0:0:1')");
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
private final LogoutHandler logoutHandler;
|
||||
private final CustomOAuth2UserService customOAuth2UserService;
|
||||
private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
|
||||
private final ClientRegistrationRepository clientRegistrationRepository;
|
||||
private final AuthenticationProvider authenticationProvider;
|
||||
|
||||
@Autowired
|
||||
public SecurityConfiguration(UserDetailsService userDetailsService, LogoutHandler logoutHandler,
|
||||
CustomOAuth2UserService customOAuth2UserService,
|
||||
OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler,
|
||||
@Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository) {
|
||||
public SecurityConfiguration(UserDetailsService userDetailsService,
|
||||
LogoutHandler logoutHandler,
|
||||
@Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository,
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.logoutHandler = logoutHandler;
|
||||
this.customOAuth2UserService = customOAuth2UserService;
|
||||
this.oAuth2AuthenticationSuccessHandler = oAuth2AuthenticationSuccessHandler;
|
||||
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||
this.authenticationProvider = authenticationProvider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -86,20 +81,16 @@ public class SecurityConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
public OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler(
|
||||
UserRepository userRepository,
|
||||
AuthenticationRepository authenticationRepository) {
|
||||
return new OAuth2AuthenticationSuccessHandler(userRepository, authenticationRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationProvider authenticationProvider() {
|
||||
final DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setPasswordEncoder(passwordEncoder());
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthFilter) throws Exception {
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http,
|
||||
JwtAuthenticationFilter jwtAuthFilter,
|
||||
OAuth2AuthenticationSuccessHandler successHandler) throws Exception {
|
||||
|
||||
if (csrfEnabled) {
|
||||
http.csrf(csrf -> csrf.ignoringRequestMatchers(antMatcher("/h2-console/**")));
|
||||
@ -107,27 +98,30 @@ public class SecurityConfiguration {
|
||||
// csrf is disabled for dev and test
|
||||
http.csrf(AbstractHttpConfigurer::disable);
|
||||
}
|
||||
|
||||
http.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin));
|
||||
|
||||
http.authorizeHttpRequests(auth ->
|
||||
auth.requestMatchers(WHITE_LIST_URL).permitAll()
|
||||
.requestMatchers(antMatcher(HttpMethod.GET, "/")).permitAll()
|
||||
.requestMatchers(antMatcher(HttpMethod.GET, API_V1_URI)).permitAll()
|
||||
.requestMatchers(antMatcher(HttpMethod.PUT, API_V1_URI)).hasRole(ADMIN_ROLE)
|
||||
.requestMatchers(antMatcher(HttpMethod.DELETE, API_V1_URI)).hasRole(ADMIN_ROLE)
|
||||
.requestMatchers(antMatcher(HttpMethod.POST, API_V1_URI)).hasRole(ADMIN_ROLE)
|
||||
.requestMatchers(antMatcher("/h2-console/**")).access(INTERNAL_ACCESS)
|
||||
.requestMatchers(antMatcher("/actuator/**")).access(INTERNAL_ACCESS)
|
||||
.requestMatchers(INTERNAL_WHITE_LIST_URL).access(INTERNAL_ACCESS)
|
||||
.anyRequest().authenticated()
|
||||
auth.requestMatchers("/oauth2/authorization/**").permitAll()
|
||||
.requestMatchers("/login/oauth2/**").permitAll()
|
||||
.requestMatchers(WHITE_LIST_URL).permitAll()
|
||||
.requestMatchers(antMatcher(HttpMethod.GET, "/")).permitAll()
|
||||
.requestMatchers(antMatcher(HttpMethod.GET, API_V1_URI)).permitAll()
|
||||
.requestMatchers(antMatcher(HttpMethod.PUT, API_V1_URI)).hasRole(ADMIN_ROLE)
|
||||
.requestMatchers(antMatcher(HttpMethod.DELETE, API_V1_URI)).hasRole(ADMIN_ROLE)
|
||||
.requestMatchers(antMatcher(HttpMethod.POST, API_V1_URI)).hasRole(ADMIN_ROLE)
|
||||
.requestMatchers(antMatcher("/h2-console/**")).access(INTERNAL_ACCESS)
|
||||
.requestMatchers(antMatcher("/actuator/**")).access(INTERNAL_ACCESS)
|
||||
.requestMatchers(INTERNAL_WHITE_LIST_URL).access(INTERNAL_ACCESS)
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
|
||||
.authenticationProvider(authenticationProvider())
|
||||
.authenticationProvider(authenticationProvider)
|
||||
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
if (clientRegistrationRepository != null) {
|
||||
http.oauth2Login(oauth -> oauth
|
||||
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
|
||||
.successHandler(oAuth2AuthenticationSuccessHandler)
|
||||
.successHandler(successHandler)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,26 +1,20 @@
|
||||
spring:
|
||||
security:
|
||||
oauth2:
|
||||
client:
|
||||
registration:
|
||||
google:
|
||||
client-id: ${GOOGLE_CLIENT_ID:dummy}
|
||||
client-secret: ${GOOGLE_CLIENT_SECRET:dummy}
|
||||
scope: openid,profile,email
|
||||
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
|
||||
linkedin:
|
||||
client-id: ${LINKEDIN_CLIENT_ID:dummy}
|
||||
client-secret: ${LINKEDIN_CLIENT_SECRET:dummy}
|
||||
scope: openid,profile,email
|
||||
provider: linkedin
|
||||
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
|
||||
client-id: 1018440977117-me57ug4lqjgvcr1mg8fq17vpc18mvnsl.apps.googleusercontent.com
|
||||
client-secret: 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
|
||||
scope:
|
||||
- profile
|
||||
- email
|
||||
provider:
|
||||
linkedin:
|
||||
authorization-uri: https://www.linkedin.com/oauth/v2/authorization
|
||||
token-uri: https://www.linkedin.com/oauth/v2/accessToken
|
||||
user-info-uri: https://api.linkedin.com/v2/userinfo
|
||||
google:
|
||||
authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
|
||||
token-uri: https://www.googleapis.com/oauth2/v4/token
|
||||
user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
|
||||
user-name-attribute: sub
|
||||
---
|
||||
spring:
|
||||
h2:
|
||||
console:
|
||||
enabled: 'false'
|
||||
@ -80,3 +74,6 @@ application:
|
||||
expiration: 86400000 # a day
|
||||
refresh-token:
|
||||
expiration: 604800000 # 7 days
|
||||
|
||||
server:
|
||||
port: 8083
|
||||
@ -17,33 +17,6 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
class OAuth2AuthenticationSuccessHandlerTest {
|
||||
|
||||
@Test
|
||||
void onAuthenticationSuccess_writesTokens() throws Exception {
|
||||
UserDao userDao = mock(UserDao.class);
|
||||
TokenDao tokenDao = mock(TokenDao.class);
|
||||
JwtUtil jwtUtil = mock(JwtUtil.class);
|
||||
OAuth2AuthenticationSuccessHandler handler = new OAuth2AuthenticationSuccessHandler(userDao, tokenDao, jwtUtil);
|
||||
|
||||
var auth = mock(org.springframework.security.core.Authentication.class);
|
||||
when(auth.getName()).thenReturn("john@ex.com");
|
||||
|
||||
UserEntity user = new UserEntity();
|
||||
when(userDao.findByEmail("john@ex.com")).thenReturn(Optional.of(user));
|
||||
when(jwtUtil.generateToken(user)).thenReturn("access");
|
||||
when(jwtUtil.generateRefreshToken(user)).thenReturn("refresh");
|
||||
|
||||
var request = mock(jakarta.servlet.http.HttpServletRequest.class);
|
||||
var response = mock(HttpServletResponse.class);
|
||||
var writer = mock(PrintWriter.class);
|
||||
when(response.getWriter()).thenReturn(writer);
|
||||
|
||||
handler.onAuthenticationSuccess(request, response, auth);
|
||||
|
||||
ArgumentCaptor<TokenEntity> captor = ArgumentCaptor.forClass(TokenEntity.class);
|
||||
verify(tokenDao).save(captor.capture());
|
||||
assertThat(captor.getValue().getToken()).isEqualTo("access");
|
||||
verify(writer).write("{\"access_token\":\"access\", \"refresh_token\":\"refresh\"}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,8 +1,47 @@
|
||||
package com.dh7789dev.xpeditis;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class CommonUtil {
|
||||
|
||||
public void sayHello() {
|
||||
System.out.println("Hello!");
|
||||
private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
|
||||
private static final String DIGITS = "0123456789";
|
||||
private static final String SPECIAL = "!@#$%^&*()-_=+[]{}|;:,.<>?";
|
||||
private static final String ALL = UPPER + LOWER + DIGITS + SPECIAL;
|
||||
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
|
||||
public static String generatePassword(int length) {
|
||||
if (length < 8) {
|
||||
throw new IllegalArgumentException("Password length should be at least 8 characters");
|
||||
}
|
||||
|
||||
StringBuilder password = new StringBuilder(length);
|
||||
|
||||
// Garantir au moins un caractère de chaque catégorie
|
||||
password.append(UPPER.charAt(random.nextInt(UPPER.length())));
|
||||
password.append(LOWER.charAt(random.nextInt(LOWER.length())));
|
||||
password.append(DIGITS.charAt(random.nextInt(DIGITS.length())));
|
||||
password.append(SPECIAL.charAt(random.nextInt(SPECIAL.length())));
|
||||
|
||||
// Remplir le reste avec des caractères aléatoires
|
||||
for (int i = 4; i < length; i++) {
|
||||
password.append(ALL.charAt(random.nextInt(ALL.length())));
|
||||
}
|
||||
|
||||
// Mélanger les caractères pour éviter un ordre prévisible
|
||||
return shuffleString(password.toString());
|
||||
}
|
||||
|
||||
private static String shuffleString(String input) {
|
||||
char[] a = input.toCharArray();
|
||||
for (int i = a.length - 1; i > 0; i--) {
|
||||
int j = random.nextInt(i + 1);
|
||||
char temp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = temp;
|
||||
}
|
||||
return new String(a);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import com.dh7789dev.xpeditis.dto.app.UserAccount;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface UserRepository {
|
||||
|
||||
@ -19,4 +20,7 @@ public interface UserRepository {
|
||||
List<UserAccount> list();
|
||||
|
||||
void delete(Long id);
|
||||
|
||||
UserAccount findOrCreateOAuthUser(String email, Map<String, Object> attributes,String password);
|
||||
|
||||
}
|
||||
|
||||
@ -5,13 +5,10 @@ import com.dh7789dev.xpeditis.dto.app.Address;
|
||||
import com.dh7789dev.xpeditis.entity.AddressEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { CompanyMapper.class })
|
||||
public interface AddressMapper {
|
||||
|
||||
AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);
|
||||
|
||||
AddressEntity addressToAddressEntity(Address address);
|
||||
|
||||
Address addressEntityToAddress(AddressEntity addressEntity);
|
||||
|
||||
@ -1,20 +1,14 @@
|
||||
package com.dh7789dev.xpeditis.mapper;
|
||||
|
||||
|
||||
import com.dh7789dev.xpeditis.dto.app.Company;
|
||||
import com.dh7789dev.xpeditis.entity.CompanyEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
|
||||
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
|
||||
public interface CompanyMapper {
|
||||
|
||||
CompanyMapper INSTANCE = Mappers.getMapper(CompanyMapper.class);
|
||||
|
||||
@Mapping(target = "createdDate", ignore = true)
|
||||
@Mapping(target = "modifiedDate", ignore = true)
|
||||
@Mapping(target = "createdBy", ignore = true)
|
||||
|
||||
@ -4,11 +4,10 @@ package com.dh7789dev.xpeditis.mapper;
|
||||
import com.dh7789dev.xpeditis.dto.app.Dimension;
|
||||
import com.dh7789dev.xpeditis.entity.DimensionEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface DimensionMapper {
|
||||
DimensionMapper INSTANCE = Mappers.getMapper(DimensionMapper.class);
|
||||
|
||||
|
||||
DimensionEntity dimensionToDimensionEntity(Dimension dimension);
|
||||
|
||||
|
||||
@ -3,11 +3,10 @@ package com.dh7789dev.xpeditis.mapper;
|
||||
import com.dh7789dev.xpeditis.dto.app.Document;
|
||||
import com.dh7789dev.xpeditis.entity.DocumentEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface DocumentMapper {
|
||||
DocumentMapper INSTANCE = Mappers.getMapper(DocumentMapper.class);
|
||||
|
||||
|
||||
DocumentEntity documentToDocumentEntity(Document document);
|
||||
Document documentEntityToDocument(DocumentEntity documentEntity);
|
||||
|
||||
@ -5,11 +5,10 @@ import com.dh7789dev.xpeditis.entity.ExportFolderEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { DocumentMapper.class, CompanyMapper.class })
|
||||
public interface ExportFolderMapper {
|
||||
ExportFolderMapper INSTANCE = Mappers.getMapper(ExportFolderMapper.class);
|
||||
|
||||
|
||||
@Mapping(target = "createdDate", ignore = true)
|
||||
@Mapping(target = "modifiedDate", ignore = true)
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
package com.dh7789dev.xpeditis.mapper;
|
||||
|
||||
|
||||
import com.dh7789dev.xpeditis.dto.app.License;
|
||||
import com.dh7789dev.xpeditis.entity.LicenseEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface LicenseMapper {
|
||||
LicenseMapper INSTANCE = Mappers.getMapper(LicenseMapper.class);
|
||||
|
||||
@Mapping(target = "createdDate", ignore = true)
|
||||
@Mapping(target = "modifiedDate", ignore = true)
|
||||
|
||||
@ -4,12 +4,10 @@ package com.dh7789dev.xpeditis.mapper;
|
||||
import com.dh7789dev.xpeditis.dto.app.Notification;
|
||||
import com.dh7789dev.xpeditis.entity.NotificationEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
|
||||
@Mapper(componentModel = "spring" , uses = { ExportFolderMapper.class })
|
||||
public interface NotificationMapper {
|
||||
NotificationMapper INSTANCE = Mappers.getMapper(NotificationMapper.class);
|
||||
|
||||
|
||||
NotificationEntity notificationToNotificationEntity(Notification notification);
|
||||
|
||||
|
||||
@ -4,11 +4,10 @@ import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
|
||||
import com.dh7789dev.xpeditis.entity.QuoteDetailEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = "spring", uses = { DimensionMapper.class })
|
||||
public interface QuoteDetailMapper {
|
||||
QuoteDetailMapper INSTANCE = Mappers.getMapper(QuoteDetailMapper.class);
|
||||
|
||||
|
||||
@Mapping(source = "quoteId", target = "quote.id")
|
||||
QuoteDetailEntity quoteDetailsToQuoteDetailsEntity(QuoteDetail quoteDetail);
|
||||
|
||||
@ -5,11 +5,10 @@ import com.dh7789dev.xpeditis.entity.QuoteEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { QuoteDetailMapper.class })
|
||||
public interface QuoteMapper {
|
||||
QuoteMapper INSTANCE = Mappers.getMapper(QuoteMapper.class);
|
||||
|
||||
|
||||
@Mapping(target = "createdDate", ignore = true)
|
||||
@Mapping(target = "modifiedDate", ignore = true)
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
package com.dh7789dev.xpeditis.mapper;
|
||||
|
||||
|
||||
import com.dh7789dev.xpeditis.dto.app.ShipmentTracking;
|
||||
import com.dh7789dev.xpeditis.entity.ShipmentTrackingEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = "spring", uses = { ExportFolderMapper.class })
|
||||
public interface ShipmentTrackingMapper {
|
||||
|
||||
ShipmentTrackingMapper INSTANCE = Mappers.getMapper(ShipmentTrackingMapper.class);
|
||||
|
||||
ShipmentTrackingEntity shipmentTrackingToShipmentTrackingEntity(ShipmentTracking shipmentTracking);
|
||||
|
||||
ShipmentTracking shipmentTrackingEntityToShipmentTracking(ShipmentTrackingEntity shipmentTrackingEntity);
|
||||
|
||||
@ -5,14 +5,10 @@ import com.dh7789dev.xpeditis.entity.UserEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
|
||||
public interface UserMapper {
|
||||
|
||||
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
|
||||
|
||||
@Mapping(target = "createdDate", ignore = true)
|
||||
@Mapping(target = "modifiedDate", ignore = true)
|
||||
@Mapping(target = "createdBy", ignore = true)
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
package com.dh7789dev.xpeditis.mapper;
|
||||
|
||||
|
||||
import com.dh7789dev.xpeditis.dto.app.VesselSchedule;
|
||||
import com.dh7789dev.xpeditis.entity.VesselScheduleEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(componentModel = "spring", uses = { ExportFolderMapper.class })
|
||||
public interface VesselScheduleMapper {
|
||||
|
||||
VesselScheduleMapper INSTANCE = Mappers.getMapper(VesselScheduleMapper.class);
|
||||
|
||||
VesselScheduleEntity vesselScheduleToVesselScheduleEntity(VesselSchedule vesselSchedule);
|
||||
|
||||
VesselSchedule vesselScheduleEntityToVesselSchedule(VesselScheduleEntity vesselScheduleEntity);
|
||||
|
||||
@ -15,6 +15,7 @@ import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Repository
|
||||
@ -103,4 +104,19 @@ public class UserJpaRepository implements UserRepository {
|
||||
public void delete(Long id) {
|
||||
userDao.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAccount findOrCreateOAuthUser(String email, Map<String, Object> attributes,String password) {
|
||||
return userMapper.userEntityToUserAccount(userDao.findByEmail(email).orElseGet(() -> {
|
||||
UserEntity newUser = new UserEntity();
|
||||
newUser.setEmail(email);
|
||||
newUser.setUsername(email);
|
||||
newUser.setFirstName((String) attributes.getOrDefault("name", "Unknown"));
|
||||
newUser.setLastName((String) attributes.getOrDefault("name", "Unknown"));
|
||||
newUser.setPassword(passwordEncoder.encode(password));
|
||||
newUser.setRole(Role.ADMIN);
|
||||
newUser.setEnabled(true);
|
||||
return userDao.save(newUser);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user