Merge pull request #44 from HideyoshiNakazone/initial-auth-service-refactor

Initial Auth Service Refactor
This commit is contained in:
2024-02-24 02:55:24 -03:00
committed by GitHub
61 changed files with 438 additions and 549 deletions

12
pom.xml
View File

@@ -9,10 +9,10 @@
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.hideyoshi</groupId> <groupId>com.hideyoshi</groupId>
<artifactId>backend-api</artifactId> <artifactId>auth-api</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>backend-api</name> <name>auth-api</name>
<description>Backend API</description> <description>Auth API for the Hideyoshi.com Project</description>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
</properties> </properties>
@@ -107,7 +107,11 @@
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<version>4.5.14</version> <version>4.5.14</version>
</dependency> </dependency>
</dependencies> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build> <build>
<plugins> <plugins>

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio; package com.hideyoshi.auth;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -1,16 +1,16 @@
package com.hideyoshi.backendportfolio.base.user.api; package com.hideyoshi.auth.base.auth.api;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.security.service.AuthService; import com.hideyoshi.auth.base.auth.service.AuthService;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO; import com.hideyoshi.auth.base.auth.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.auth.base.auth.service.UserService;
import com.hideyoshi.backendportfolio.microservice.storageService.enums.FileTypeEnum; import com.hideyoshi.auth.microservice.storageService.enums.FileTypeEnum;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceUploadResponse; import com.hideyoshi.auth.microservice.storageService.model.StorageServiceUploadResponse;
import com.hideyoshi.backendportfolio.microservice.storageService.service.StorageService; import com.hideyoshi.auth.microservice.storageService.service.StorageService;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException; import com.hideyoshi.auth.util.exception.BadRequestException;
import com.hideyoshi.backendportfolio.util.guard.UserResourceGuard; import com.hideyoshi.auth.util.guard.UserResourceGuard;
import com.hideyoshi.backendportfolio.util.guard.UserResourceGuardEnum; import com.hideyoshi.auth.util.guard.UserResourceGuardEnum;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -20,7 +20,6 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
@@ -59,9 +58,14 @@ public class UserController {
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<AuthDTO> refreshAccessToken( public ResponseEntity<AuthDTO> refreshAccessToken(
@RequestBody @Valid TokenDTO refreshToken, @RequestBody @Valid TokenDTO refreshToken,
HttpServletRequest request, HttpServletRequest request) {
HttpServletResponse response) { return ResponseEntity.ok(this.authService.refreshAccessToken(refreshToken.getToken(), request));
return ResponseEntity.ok(this.authService.refreshAccessToken(refreshToken.getToken(), request, response)); }
@PostMapping("/login/validate")
@UserResourceGuard(accessType = UserResourceGuardEnum.USER)
public ResponseEntity<AuthDTO> validateAccessToken(HttpServletRequest request) {
return ResponseEntity.ok(this.authService.validateAccessToken(request));
} }
@DeleteMapping("/delete") @DeleteMapping("/delete")

View File

@@ -1,11 +1,13 @@
package com.hideyoshi.backendportfolio.base.security.config; package com.hideyoshi.auth.base.auth.config;
import com.hideyoshi.backendportfolio.base.config.RestAuthenticationEntryPointConfig; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.security.filter.CustomAuthenticationFilter; import com.hideyoshi.auth.base.config.RestAuthenticationEntryPointConfig;
import com.hideyoshi.backendportfolio.base.security.filter.CustomAuthorizationFilter; import com.hideyoshi.auth.base.auth.filter.CustomAuthenticationFilter;
import com.hideyoshi.backendportfolio.base.security.oauth.repo.OAuthRequestRepository; import com.hideyoshi.auth.base.auth.filter.CustomAuthorizationFilter;
import com.hideyoshi.backendportfolio.base.security.service.AuthService; import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.backendportfolio.util.exception.AuthenticationInvalidException; import com.hideyoshi.auth.base.auth.oauth.repo.OAuthRequestRepository;
import com.hideyoshi.auth.base.auth.service.AuthService;
import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -27,6 +29,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Log4j2 @Log4j2
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@@ -95,11 +99,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal(); OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
this.authService.loginOAuthUser( AuthDTO authUser = this.authService.loginOAuthUser(oauthUser, request);
request,
response, response.setContentType(APPLICATION_JSON_VALUE);
oauthUser new ObjectMapper()
); .writeValue(response.getOutputStream(), authUser);
} }

View File

@@ -1,5 +1,8 @@
package com.hideyoshi.backendportfolio.base.user.entity; package com.hideyoshi.auth.base.auth.entity;
import lombok.Getter;
@Getter
public enum Provider { public enum Provider {
GOOGLE("google"), GOOGLE("google"),
@@ -8,7 +11,7 @@ public enum Provider {
LOCAL("local"); LOCAL("local");
private String name; private final String name;
Provider(String name) { Provider(String name) {
this.name = name; this.name = name;
@@ -23,8 +26,4 @@ public enum Provider {
throw new IllegalArgumentException("Argument not valid."); throw new IllegalArgumentException("Argument not valid.");
} }
public String getName() {
return name;
}
} }

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.user.entity; package com.hideyoshi.auth.base.auth.entity;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.user.entity; package com.hideyoshi.auth.base.auth.entity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View File

@@ -1,8 +1,10 @@
package com.hideyoshi.backendportfolio.base.security.filter; package com.hideyoshi.auth.base.auth.filter;
import com.hideyoshi.backendportfolio.base.config.RestAuthenticationEntryPointConfig; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.security.service.AuthService; import com.hideyoshi.auth.base.config.RestAuthenticationEntryPointConfig;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.auth.base.auth.service.AuthService;
import com.hideyoshi.auth.base.auth.model.UserDTO;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -15,6 +17,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Log4j2 @Log4j2
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@@ -50,12 +54,16 @@ public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFi
@Override @Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException { protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException {
this.authService.loginUser( AuthDTO authUser = this.authService.loginUser(
request, request,
response, response,
(UserDTO) authentication.getPrincipal() (UserDTO) authentication.getPrincipal()
); );
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authUser);
} }
} }

View File

@@ -1,10 +1,9 @@
package com.hideyoshi.backendportfolio.base.security.filter; package com.hideyoshi.auth.base.auth.filter;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.security.service.AuthService; import com.hideyoshi.auth.base.auth.service.AuthService;
import com.hideyoshi.backendportfolio.util.exception.AuthenticationInvalidException; import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import com.hideyoshi.backendportfolio.util.exception.AuthenticationInvalidExceptionDetails; import com.hideyoshi.auth.util.exception.AuthenticationInvalidExceptionDetails;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@@ -78,7 +77,8 @@ public class CustomAuthorizationFilter extends OncePerRequestFilter {
private UsernamePasswordAuthenticationToken validateUserAccess(String authorizationHeader) { private UsernamePasswordAuthenticationToken validateUserAccess(String authorizationHeader) {
if (Objects.nonNull(authorizationHeader) && authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) { if (Objects.nonNull(authorizationHeader) && authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) {
return this.authService.verifyAccessToken(authorizationHeader); String accessToken = authorizationHeader.substring(AUTHORIZATION_TYPE_STRING.length());
return this.authService.extractAccessTokenInfo(accessToken);
} else { } else {
throw new AuthenticationInvalidException("Access denied"); throw new AuthenticationInvalidException("Access denied");
} }

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.security.interceptor; package com.hideyoshi.auth.base.auth.interceptor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@@ -1,10 +1,9 @@
package com.hideyoshi.backendportfolio.base.security.interceptor; package com.hideyoshi.auth.base.auth.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.auth.base.auth.service.UserService;
import com.hideyoshi.backendportfolio.util.exception.AuthenticationInvalidException; import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException; import com.hideyoshi.auth.util.guard.UserResourceGuard;
import com.hideyoshi.backendportfolio.util.guard.UserResourceGuard;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@@ -1,11 +1,9 @@
package com.hideyoshi.backendportfolio.base.security.model; package com.hideyoshi.auth.base.auth.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.user.model; package com.hideyoshi.auth.base.auth.model;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.user.model; package com.hideyoshi.auth.base.auth.model;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

View File

@@ -1,14 +1,14 @@
package com.hideyoshi.backendportfolio.base.user.model; package com.hideyoshi.auth.base.auth.model;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.backendportfolio.base.user.entity.User; import com.hideyoshi.auth.base.auth.entity.User;
import com.hideyoshi.backendportfolio.util.validator.email.unique.UniqueEmail; import com.hideyoshi.auth.util.validator.email.unique.UniqueEmail;
import com.hideyoshi.backendportfolio.util.validator.email.valid.ValidEmail; import com.hideyoshi.auth.util.validator.email.valid.ValidEmail;
import com.hideyoshi.backendportfolio.util.validator.password.ValidPassword; import com.hideyoshi.auth.util.validator.password.ValidPassword;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.base.security.oauth.mapper; package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.base.security.oauth.mapper; package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User;

View File

@@ -0,0 +1,13 @@
package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.auth.base.auth.entity.Provider;
public interface OAuthMap {
String getPrincipal();
String getProfilePicture();
Provider getProvider();
}

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.base.security.oauth.mapper; package com.hideyoshi.auth.base.auth.oauth.mapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import lombok.Getter; import lombok.Getter;
public enum OAuthMapper { public enum OAuthMapper {
@@ -9,26 +9,26 @@ public enum OAuthMapper {
GITHUB(GithubOAuthMap.class, Provider.GITHUB); GITHUB(GithubOAuthMap.class, Provider.GITHUB);
private final Class oAuthMap; private final Class<? extends OAuthMap> oAuthMap;
@Getter @Getter
private final Provider provider; private final Provider provider;
private OAuthMapper(Class oAuthMap, Provider provider) { private OAuthMapper(Class<? extends OAuthMap> oAuthMap, Provider provider) {
this.oAuthMap = oAuthMap; this.oAuthMap = oAuthMap;
this.provider = provider; this.provider = provider;
} }
public static OAuthMapper byValue(String name) { public static OAuthMapper byValue(Provider provider) {
for (OAuthMapper e : values()) { for (OAuthMapper e : values()) {
if (e.getProvider().getName().equals(name)) { if (e.getProvider().equals(provider)) {
return e; return e;
} }
} }
throw new IllegalArgumentException("Argument not valid."); throw new IllegalArgumentException("Argument not valid.");
} }
public Class getMap() { public Class<? extends OAuthMap> getMap() {
return oAuthMap; return oAuthMap;
} }

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.security.oauth.repo; package com.hideyoshi.auth.base.auth.oauth.repo;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.base.user.repo; package com.hideyoshi.auth.base.auth.repo;
import com.hideyoshi.backendportfolio.base.user.entity.User; import com.hideyoshi.auth.base.auth.entity.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;

View File

@@ -0,0 +1,235 @@
package com.hideyoshi.auth.base.auth.service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.auth.base.auth.oauth.mapper.OAuthMap;
import com.hideyoshi.auth.base.auth.oauth.mapper.OAuthMapper;
import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.auth.base.auth.model.TokenDTO;
import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.auth.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.auth.microservice.storageService.service.StorageService;
import com.hideyoshi.auth.util.exception.BadRequestException;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.Arrays.stream;
@Log4j2
@Service
@RequiredArgsConstructor
public class AuthService {
private static final String AUTHORIZATION_TYPE_STRING = "Bearer ";
private final UserService userService;
private final StorageService storageService;
@Value("${com.hideyoshi.tokenSecret}")
private String TOKEN_SECRET;
@Value("${com.hideyoshi.accessTokenDuration}")
private Integer ACCESS_TOKEN_DURATION;
@Value("${com.hideyoshi.refreshTokenDuration}")
private Integer REFRESH_TOKEN_DURATION;
public AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request) {
user.setProvider(Provider.LOCAL);
UserDTO authenticatedUser = this.userService.saveUser(user);
authenticatedUser.setProfilePictureUrl(this.extractProfilePictureUrl(authenticatedUser));
return this.generateNewAuthenticatedUser(
authenticatedUser,
request
);
}
public AuthDTO loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException {
user.setProfilePictureUrl(this.extractProfilePictureUrl(user));
return this.generateNewAuthenticatedUser(
user,
request
);
}
public AuthDTO loginOAuthUser(OAuth2User oauthUser, HttpServletRequest request) {
Provider clientProvider = Provider.byValue(
this.getClientFromUrl(request.getRequestURL().toString())
);
OAuthMap oauthMap = this.generateOAuthMap(clientProvider, oauthUser);
return this.processOAuthPostLogin(
this.generateAuthenticatedUserFromOAuth(oauthMap, oauthUser),
request
);
}
public AuthDTO refreshAccessToken(String requestToken, HttpServletRequest request) {
DecodedJWT decodedJWT = this.decodeToken(requestToken)
.orElseThrow(() -> new BadRequestException("Invalid Token"));
String username = decodedJWT.getSubject();
UserDTO user = this.userService.getUser(username);
user.setProfilePictureUrl(this.extractProfilePictureUrl(user));
return this.refreshAuthenticatedUser(user, request, new TokenDTO(requestToken, decodedJWT.getExpiresAt()));
}
public AuthDTO validateAccessToken(HttpServletRequest request) {
UserDTO user = this.getLoggedUser();
user.setProfilePictureUrl(this.extractProfilePictureUrl(user));
return this.generateNewAuthenticatedUser(user, request);
}
public UserDTO getLoggedUser() {
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return userService.getUser(username);
}
public UsernamePasswordAuthenticationToken extractAccessTokenInfo(String accessToken) {
DecodedJWT decodedJWT = this.decodeToken(accessToken)
.orElseThrow(() -> new BadRequestException("Invalid Token"));
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
});
return new UsernamePasswordAuthenticationToken(username, null, authorities);
}
private Optional<DecodedJWT> decodeToken(String token) {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
JWTVerifier verifier = JWT.require(algorithm).build();
try {
return Optional.of(verifier.verify(token));
} catch (Exception e) {
log.warn("Token verification failed: {}", e.getMessage());
}
return Optional.empty();
}
private AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) {
if (Objects.nonNull(user.getId())) {
this.userService.alterUser(user.getId(), user);
} else {
this.userService.saveUser(user);
}
return this.generateNewAuthenticatedUser(user, request);
}
private String getClientFromUrl(String url) {
String[] urlPartition = url.split("/");
return urlPartition[urlPartition.length - 1];
}
private OAuthMap generateOAuthMap(Provider clientProvider, OAuth2User oauthUser) {
try {
return OAuthMapper.byValue(clientProvider).getMap()
.getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser);
} catch (Exception e) {
throw new BadRequestException("Unsupported OAuth Client.");
}
}
private String extractProfilePictureUrl(UserDTO user) {
return this.storageService.getFileUrl(user.getUsername(), "profile")
.map(StorageServiceDownloadResponse::getPresignedUrl)
.orElse(null);
}
private UserDTO generateAuthenticatedUserFromOAuth(OAuthMap oauthMap, OAuth2User oauthUser) {
UserDTO user;
try {
user = this.userService.getUser(oauthMap.getPrincipal());
} catch (BadRequestException e) {
user = UserDTO.builder()
.name(oauthUser.getAttribute("name"))
.username(oauthMap.getPrincipal())
.email(oauthUser.getAttribute("email"))
.roles(List.of(Role.USER))
.provider(oauthMap.getProvider())
.build();
}
user.setProfilePictureUrl(oauthMap.getProfilePicture());
return user;
}
private AuthDTO generateNewAuthenticatedUser(UserDTO user, HttpServletRequest request) {
HttpSession httpSession = request.getSession();
AuthDTO authObject = new AuthDTO(
user,
this.generateToken(user, request, ACCESS_TOKEN_DURATION),
this.generateToken(user, request, REFRESH_TOKEN_DURATION)
);
httpSession.setAttribute("user", authObject);
return authObject;
}
private AuthDTO refreshAuthenticatedUser(UserDTO user, HttpServletRequest request, TokenDTO refreshToken) {
HttpSession httpSession = request.getSession();
AuthDTO authObject = new AuthDTO(
user,
this.generateToken(user, request, ACCESS_TOKEN_DURATION),
refreshToken
);
httpSession.setAttribute("user", authObject);
return authObject;
}
private TokenDTO generateToken(@Valid UserDTO user, HttpServletRequest request, Integer duration) {
Date expirationDate = new Date(System.currentTimeMillis() + duration);
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
String token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(expirationDate)
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()))
.sign(algorithm);
return new TokenDTO(token, expirationDate);
}
}

View File

@@ -1,14 +1,15 @@
package com.hideyoshi.backendportfolio.base.user.service; package com.hideyoshi.auth.base.auth.service;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.backendportfolio.base.user.entity.User; import com.hideyoshi.auth.base.auth.entity.User;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.repo.UserRepository; import com.hideyoshi.auth.base.auth.repo.UserRepository;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException; import com.hideyoshi.auth.util.exception.BadRequestException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -22,13 +23,12 @@ import java.util.stream.Collectors;
@Service @Service
@Transactional @Transactional
@RequiredArgsConstructor @RequiredArgsConstructor
public class UserServiceImpl implements UserService { public class UserService implements UserDetailsService {
private final UserRepository userRepo; private final UserRepository userRepo;
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
@Override
public UserDTO saveUser(@Valid UserDTO user) { public UserDTO saveUser(@Valid UserDTO user) {
this.userRepo.findByUsername(user.getUsername()).ifPresent(userOnDB -> { this.userRepo.findByUsername(user.getUsername()).ifPresent(userOnDB -> {
@@ -45,7 +45,6 @@ public class UserServiceImpl implements UserService {
return userSaved; return userSaved;
} }
@Override
public void alterUser(Long id, @Valid UserDTO user) { public void alterUser(Long id, @Valid UserDTO user) {
this.userRepo.findById(id).ifPresentOrElse(userOnDB -> { this.userRepo.findById(id).ifPresentOrElse(userOnDB -> {
@@ -57,7 +56,6 @@ public class UserServiceImpl implements UserService {
}); });
} }
@Override
public void deleteUser(Long id) { public void deleteUser(Long id) {
this.userRepo.findById(id).ifPresentOrElse(userOnDB -> { this.userRepo.findById(id).ifPresentOrElse(userOnDB -> {
@@ -68,7 +66,6 @@ public class UserServiceImpl implements UserService {
} }
@Override
public void addRoleToUser(Long id, String roleName) { public void addRoleToUser(Long id, String roleName) {
UserDTO userOnDB = this.getUser(id); UserDTO userOnDB = this.getUser(id);
@@ -89,7 +86,6 @@ public class UserServiceImpl implements UserService {
} }
@Override
public void removeRoleFromUser(Long id, String roleName) { public void removeRoleFromUser(Long id, String roleName) {
UserDTO userOnDB = this.getUser(id); UserDTO userOnDB = this.getUser(id);
@@ -109,7 +105,6 @@ public class UserServiceImpl implements UserService {
} }
} }
@Override
public UserDTO getUser(Long id) { public UserDTO getUser(Long id) {
log.info(String.format("Fetching user with id: %o", id)); log.info(String.format("Fetching user with id: %o", id));
@@ -119,7 +114,6 @@ public class UserServiceImpl implements UserService {
); );
} }
@Override
public UserDTO getUser(String username) { public UserDTO getUser(String username) {
log.info(String.format("Fetching user: %s", username)); log.info(String.format("Fetching user: %s", username));
@@ -129,7 +123,6 @@ public class UserServiceImpl implements UserService {
); );
} }
@Override
public List<UserDTO> getUsers() { public List<UserDTO> getUsers() {
log.info("Fetching all users."); log.info("Fetching all users.");
@@ -138,7 +131,6 @@ public class UserServiceImpl implements UserService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override
public UserDetails loadUserByUsername(String username) { public UserDetails loadUserByUsername(String username) {
return this.getUser(username); return this.getUser(username);
} }

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.config; package com.hideyoshi.auth.base.config;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@@ -1,10 +1,10 @@
package com.hideyoshi.backendportfolio.base.config; package com.hideyoshi.auth.base.config;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.repo.UserRepository; import com.hideyoshi.auth.base.auth.repo.UserRepository;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.auth.base.auth.service.UserService;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.base.config; package com.hideyoshi.auth.base.config;
import com.hideyoshi.backendportfolio.util.exception.AuthenticationInvalidException; import com.hideyoshi.auth.util.exception.AuthenticationInvalidException;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.config; package com.hideyoshi.auth.base.config;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@@ -1,7 +1,7 @@
package com.hideyoshi.backendportfolio.base.session.api; package com.hideyoshi.auth.base.session.api;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import com.hideyoshi.auth.base.auth.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.session.service.SessionManagerService; import com.hideyoshi.auth.base.session.service.SessionManagerService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.base.session.service; package com.hideyoshi.auth.base.session.service;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import com.hideyoshi.auth.base.auth.model.AuthDTO;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.base.session.service; package com.hideyoshi.auth.base.session.service;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import com.hideyoshi.auth.base.auth.model.AuthDTO;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

View File

@@ -1,8 +1,8 @@
package com.hideyoshi.backendportfolio.healthChecker.api; package com.hideyoshi.auth.healthChecker.api;
import com.hideyoshi.backendportfolio.util.guard.UserResourceGuard; import com.hideyoshi.auth.util.guard.UserResourceGuard;
import com.hideyoshi.backendportfolio.util.guard.UserResourceGuardEnum; import com.hideyoshi.auth.util.guard.UserResourceGuardEnum;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.microservice.storageService.config; package com.hideyoshi.auth.microservice.storageService.config;
import lombok.Getter; import lombok.Getter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;

View File

@@ -1,7 +1,7 @@
package com.hideyoshi.backendportfolio.microservice.storageService.enums; package com.hideyoshi.auth.microservice.storageService.enums;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException; import com.hideyoshi.auth.util.exception.BadRequestException;
import lombok.Getter; import lombok.Getter;
@Getter @Getter

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.microservice.storageService.enums; package com.hideyoshi.auth.microservice.storageService.enums;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.microservice.storageService.model; package com.hideyoshi.auth.microservice.storageService.model;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.microservice.storageService.model; package com.hideyoshi.auth.microservice.storageService.model;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;

View File

@@ -1,11 +1,11 @@
package com.hideyoshi.backendportfolio.microservice.storageService.service; package com.hideyoshi.auth.microservice.storageService.service;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.microservice.storageService.config.StorageServiceConfig; import com.hideyoshi.auth.microservice.storageService.config.StorageServiceConfig;
import com.hideyoshi.backendportfolio.microservice.storageService.enums.FileTypeEnum; import com.hideyoshi.auth.microservice.storageService.enums.FileTypeEnum;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceDownloadResponse; import com.hideyoshi.auth.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceUploadResponse; import com.hideyoshi.auth.microservice.storageService.model.StorageServiceUploadResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
@@ -63,7 +63,7 @@ public class StorageService {
} }
public Optional<StorageServiceDownloadResponse> getFileUrl(String username, String filePostfix) { public Optional<StorageServiceDownloadResponse> getFileUrl(String username, String filePostfix) {
URI uri = null; URI uri;
try { try {
uri = new URIBuilder(storageServiceConfig.getFileServicePath() + "/file") uri = new URIBuilder(storageServiceConfig.getFileServicePath() + "/file")
.addParameter(PARAMETER_USERNAME, username) .addParameter(PARAMETER_USERNAME, username)

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.exception; package com.hideyoshi.auth.util.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.exception; package com.hideyoshi.auth.util.exception;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.exception; package com.hideyoshi.auth.util.exception;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.exception; package com.hideyoshi.auth.util.exception;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.exception; package com.hideyoshi.auth.util.exception;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.exception; package com.hideyoshi.auth.util.exception;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.guard; package com.hideyoshi.auth.util.guard;
import java.lang.annotation.*; import java.lang.annotation.*;

View File

@@ -1,9 +1,9 @@
package com.hideyoshi.backendportfolio.util.guard; package com.hideyoshi.auth.util.guard;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.auth.base.auth.service.UserService;
import lombok.Getter; import lombok.Getter;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.util.guard; package com.hideyoshi.auth.util.guard;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.model.UserDTO;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.util.handler; package com.hideyoshi.auth.util.handler;
import com.hideyoshi.backendportfolio.util.exception.*; import com.hideyoshi.auth.util.exception.*;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.util.validator.email.unique; package com.hideyoshi.auth.util.validator.email.unique;
import com.hideyoshi.backendportfolio.base.user.repo.UserRepository; import com.hideyoshi.auth.base.auth.repo.UserRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.validator.email.unique; package com.hideyoshi.auth.util.validator.email.unique;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.validator.email.valid; package com.hideyoshi.auth.util.validator.email.valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.util.validator.email.valid; package com.hideyoshi.auth.util.validator.email.valid;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.util.validator.password; package com.hideyoshi.auth.util.validator.password;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;

View File

@@ -1,6 +1,6 @@
package com.hideyoshi.backendportfolio.util.validator.password; package com.hideyoshi.auth.util.validator.password;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.entity.Provider;
import javax.validation.Constraint; import javax.validation.Constraint;
import javax.validation.Payload; import javax.validation.Payload;

View File

@@ -1,13 +0,0 @@
package com.hideyoshi.backendportfolio.base.security.oauth.mapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider;
public interface OAuthMap {
String getPrincipal();
String getProfilePicture();
Provider getProvider();
}

View File

@@ -1,40 +0,0 @@
package com.hideyoshi.backendportfolio.base.security.service;
import com.auth0.jwt.algorithms.Algorithm;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.HashMap;
public interface AuthService {
TokenDTO generateAccessToken(@Valid UserDTO user, Algorithm algorithm, HttpServletRequest request);
TokenDTO generateRefreshToken(@Valid UserDTO user, Algorithm algorithm, HttpServletRequest request);
HashMap<String, TokenDTO> generateTokens(@Valid UserDTO user, Algorithm algorithm, HttpServletRequest request);
UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader);
AuthDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response);
AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request);
AuthDTO generateUserWithTokens(UserDTO user, HttpServletRequest request);
AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request);
void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException;
void loginOAuthUser(HttpServletRequest request, HttpServletResponse response, OAuth2User user) throws IOException;
UserDTO getLoggedUser();
}

View File

@@ -1,290 +0,0 @@
package com.hideyoshi.backendportfolio.base.security.service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMap;
import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.service.UserService;
import com.hideyoshi.backendportfolio.microservice.storageService.service.StorageService;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerExceptionResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.Arrays.stream;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Log4j2
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {
private static final String AUTHORIZATION_TYPE_STRING = "Bearer ";
private final UserService userService;
private final StorageService storageService;
@Value("${com.hideyoshi.tokenSecret}")
private String TOKEN_SECRET;
@Value("${com.hideyoshi.accessTokenDuration}")
private Integer ACCESS_TOKEN_DURATION;
@Value("${com.hideyoshi.refreshTokenDuration}")
private Integer REFRESH_TOKEN_DURATION;
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
public TokenDTO generateAccessToken(@Valid UserDTO user, Algorithm algorithm, HttpServletRequest request) {
Date expirationDate = new Date(System.currentTimeMillis() + ACCESS_TOKEN_DURATION);
String accessToken = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(expirationDate)
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()))
.sign(algorithm);
return new TokenDTO(accessToken, expirationDate);
}
@Override
public TokenDTO generateRefreshToken(@Valid UserDTO user, Algorithm algorithm, HttpServletRequest request) {
Date expirationDate = new Date(System.currentTimeMillis() + REFRESH_TOKEN_DURATION);
String refreshToken = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(expirationDate)
.withIssuer(request.getRequestURL().toString())
.sign(algorithm);
return new TokenDTO(refreshToken, expirationDate);
}
@Override
public HashMap<String, TokenDTO> generateTokens(@Valid UserDTO user, Algorithm algorithm, HttpServletRequest request) {
TokenDTO accessToken = generateAccessToken(user, algorithm, request);
TokenDTO refreshToken = generateRefreshToken(user, algorithm, request);
HashMap<String, TokenDTO> tokens = new HashMap<>();
tokens.put("accessToken", accessToken);
tokens.put("refreshToken", refreshToken);
return tokens;
}
@Override
public UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader) {
if (!authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) {
return null;
}
String authorizationToken = authorizationHeader.substring(AUTHORIZATION_TYPE_STRING.length());
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(authorizationToken);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
});
return new UsernamePasswordAuthenticationToken(username, null, authorities);
}
@Override
public AuthDTO generateUserWithTokens(UserDTO user, HttpServletRequest request) {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
HashMap<String, TokenDTO> tokens = this.generateTokens(user, algorithm, request);
HttpSession httpSession = request.getSession();
AuthDTO authObject = new AuthDTO(user, tokens.get("accessToken"), tokens.get("refreshToken"));
httpSession.setAttribute("user", authObject);
return authObject;
}
@Override
public AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request) {
user.setProvider(Provider.LOCAL);
UserDTO authenticatedUser = this.userService.saveUser(user);
var profilePicture = this.storageService.getFileUrl(authenticatedUser.getUsername(), "profile");
profilePicture.ifPresent(
storageServiceDownloadResponse -> authenticatedUser.setProfilePictureUrl(storageServiceDownloadResponse.getPresignedUrl())
);
return this.generateUserWithTokens(
authenticatedUser,
request
);
}
@Override
public void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException {
var profilePicture = this.storageService.getFileUrl(user.getUsername(), "profile");
profilePicture.ifPresent(
storageServiceDownloadResponse -> user.setProfilePictureUrl(storageServiceDownloadResponse.getPresignedUrl())
);
AuthDTO authObject = this.generateUserWithTokens(
user,
request
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authObject);
}
@Override
public AuthDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response) {
if (!Objects.nonNull(refreshToken)) {
resolver.resolveException(
request,
response,
null,
new BadRequestException("Invalid Refresh Token. Please authenticate first.")
);
}
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(refreshToken);
UserDTO user = this.userService.getUser(decodedJWT.getSubject());
var profilePicture = this.storageService.getFileUrl(user.getUsername(), "profile");
profilePicture.ifPresent(
storageServiceDownloadResponse -> user.setProfilePictureUrl(storageServiceDownloadResponse.getPresignedUrl())
);
HttpSession httpSession = request.getSession();
AuthDTO authenticatedUser = new AuthDTO(
user,
this.generateAccessToken(user, algorithm, request),
new TokenDTO(
refreshToken,
decodedJWT.getExpiresAt()
)
);
httpSession.setAttribute("user", authenticatedUser);
return authenticatedUser;
}
@Override
public AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) {
if (Objects.nonNull(user.getId())) {
this.userService.alterUser(user.getId(), user);
} else {
this.userService.saveUser(user);
}
return this.generateUserWithTokens(user, request);
}
@Override
public void loginOAuthUser(HttpServletRequest request,
HttpServletResponse response,
OAuth2User oauthUser) throws IOException {
String clientId = this.getClientFromUrl(request.getRequestURL().toString());
OAuthMap oauthMap = this.generateOAuthMap(clientId, oauthUser);
AuthDTO authObject = this.processOAuthPostLogin(
this.generateUserFromAuthUser(oauthMap, oauthUser),
request
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authObject);
}
@Override
public UserDTO getLoggedUser() {
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return userService.getUser(username);
}
private String getClientFromUrl(String url) {
String[] urlPartition = url.split("/");
return urlPartition[urlPartition.length - 1];
}
private OAuthMap generateOAuthMap(String clientId, OAuth2User oauthUser) {
try {
return (OAuthMap) OAuthMapper.byValue(clientId).getMap()
.getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser);
} catch (Exception e) {
throw new BadRequestException("Unsupported OAuth Client.");
}
}
private UserDTO generateUserFromAuthUser(OAuthMap oauthMap, OAuth2User oauthUser) {
UserDTO user = null;
try {
user = this.userService.getUser(oauthMap.getPrincipal());
} catch (BadRequestException e) {
user = UserDTO.builder()
.name(oauthUser.getAttribute("name"))
.username(oauthMap.getPrincipal())
.email(oauthUser.getAttribute("email"))
.roles(Arrays.asList(Role.USER))
.provider(oauthMap.getProvider())
.build();
}
user.setProfilePictureUrl(oauthMap.getProfilePicture());
return user;
}
}

View File

@@ -1,26 +0,0 @@
package com.hideyoshi.backendportfolio.base.user.service;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import org.springframework.security.core.userdetails.UserDetailsService;
import javax.validation.Valid;
import java.util.List;
public interface UserService extends UserDetailsService {
UserDTO saveUser(@Valid UserDTO user);
void alterUser(Long id, @Valid UserDTO user);
void deleteUser(Long id);
void addRoleToUser(Long id, String roleName);
void removeRoleFromUser(Long id, String roleName);
UserDTO getUser(Long id);
UserDTO getUser(String username);
List<UserDTO> getUsers();
}

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio; package com.hideyoshi.auth;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

View File

@@ -1,9 +1,10 @@
package com.hideyoshi.backendportfolio.base.user.repo; package com.hideyoshi.auth.base.user.repo;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.repo.UserRepository;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.User; import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.entity.User;
import com.hideyoshi.auth.base.auth.model.UserDTO;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -1,11 +1,12 @@
package com.hideyoshi.backendportfolio.base.user.service; package com.hideyoshi.auth.base.user.service;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.auth.base.auth.service.UserService;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.auth.base.auth.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.User; import com.hideyoshi.auth.base.auth.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.auth.base.auth.entity.User;
import com.hideyoshi.backendportfolio.base.user.repo.UserRepository; import com.hideyoshi.auth.base.auth.model.UserDTO;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException; import com.hideyoshi.auth.base.auth.repo.UserRepository;
import com.hideyoshi.auth.util.exception.BadRequestException;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@@ -31,9 +32,9 @@ import static org.mockito.Mockito.verify;
@DataJpaTest @DataJpaTest
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class UserServiceImplTest { class UserServiceTest {
private UserServiceImpl underTest; private UserService underTest;
@Mock @Mock
private UserRepository userRepository; private UserRepository userRepository;
@@ -42,7 +43,7 @@ class UserServiceImplTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
this.underTest = new UserServiceImpl(userRepository, passwordEncoder); this.underTest = new UserService(userRepository, passwordEncoder);
} }
@Test @Test

View File

@@ -1,10 +1,10 @@
package com.hideyoshi.backendportfolio.microservice.storageService.service; package com.hideyoshi.auth.microservice.storageService.service;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.microservice.storageService.config.StorageServiceConfig; import com.hideyoshi.auth.microservice.storageService.config.StorageServiceConfig;
import com.hideyoshi.backendportfolio.microservice.storageService.enums.FileTypeEnum; import com.hideyoshi.auth.microservice.storageService.enums.FileTypeEnum;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceDownloadResponse; import com.hideyoshi.auth.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceUploadResponse; import com.hideyoshi.auth.microservice.storageService.model.StorageServiceUploadResponse;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;