diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/security/model/AuthDTO.java b/src/main/java/com/hideyoshi/backendportfolio/base/security/model/AuthDTO.java new file mode 100644 index 0000000..21e765f --- /dev/null +++ b/src/main/java/com/hideyoshi/backendportfolio/base/security/model/AuthDTO.java @@ -0,0 +1,34 @@ +package com.hideyoshi.backendportfolio.base.security.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.hideyoshi.backendportfolio.base.user.model.TokenDTO; +import com.hideyoshi.backendportfolio.base.user.model.UserDTO; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +@Data +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AuthDTO implements Serializable { + + private UserDTO user; + + private TokenDTO accessToken; + + private TokenDTO refreshToken; + + public AuthDTO(UserDTO user) { + this.user = user.toResponse(); + } + + public AuthDTO(UserDTO user, TokenDTO accessToken, TokenDTO refreshToken) { + this.user = user.toResponse(); + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + +} diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthService.java b/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthService.java index 7e0afd8..4f11ecb 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthService.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthService.java @@ -1,6 +1,7 @@ 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; @@ -22,13 +23,13 @@ public interface AuthService { UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader); - UserDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response); + AuthDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response); - UserDTO signupUser(@Valid UserDTO user, HttpServletRequest request); + AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request); - UserDTO generateUserWithTokens(UserDTO user, HttpServletRequest request); + AuthDTO generateUserWithTokens(UserDTO user, HttpServletRequest request); - UserDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request); + AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request); void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException; diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthServiceImpl.java b/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthServiceImpl.java index 8c3a00b..9b8cd41 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthServiceImpl.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/security/service/AuthServiceImpl.java @@ -5,6 +5,7 @@ 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; @@ -102,7 +103,7 @@ public class AuthServiceImpl implements AuthService { TokenDTO accessToken = generateAccessToken(user, algorithm, request); TokenDTO refreshToken = generateRefreshToken(user, algorithm, request); - HashMap tokens = new HashMap<>(); + HashMap tokens = new HashMap<>(); tokens.put("accessToken", accessToken); tokens.put("refreshToken", refreshToken); @@ -112,54 +113,81 @@ public class AuthServiceImpl implements AuthService { @Override public UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader) { - if (authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) { - - 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 authorities = new ArrayList<>(); - stream(roles).forEach(role -> { - authorities.add(new SimpleGrantedAuthority(role)); - }); - return new UsernamePasswordAuthenticationToken(username, null, authorities); + if (!authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) { + return null; } - 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 authorities = new ArrayList<>(); + stream(roles).forEach(role -> { + authorities.add(new SimpleGrantedAuthority(role)); + }); + + return new UsernamePasswordAuthenticationToken(username, null, authorities); } @Override - public UserDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response) { + public AuthDTO generateUserWithTokens(UserDTO user, HttpServletRequest request) { - if (Objects.nonNull(refreshToken)) { + Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes()); - Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes()); + HashMap tokens = this.generateTokens(user, algorithm, request); - JWTVerifier verifier = JWT.require(algorithm).build(); - DecodedJWT decodedJWT = verifier.verify(refreshToken); + HttpSession httpSession = request.getSession(); + AuthDTO authObject = new AuthDTO(user, tokens.get("accessToken"), tokens.get("refreshToken")); - UserDTO user = this.userService.getUser(decodedJWT.getSubject()); + httpSession.setAttribute("user", authObject); - if (Objects.nonNull(user)) { + return authObject; + } - HttpSession httpSession = request.getSession(); - UserDTO authenticatedUser = user.toResponse( - this.generateAccessToken(user, algorithm, request), - new TokenDTO( - refreshToken, - decodedJWT.getExpiresAt() - ) - ); - httpSession.setAttribute("user", authenticatedUser); + @Override + public AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request) { - return authenticatedUser; - } + user.setProvider(Provider.LOCAL); - } else { + UserDTO authenticatedUser = this.userService.saveUser(user); + authenticatedUser.setProfilePictureUrl( + this.storageService.getFileUrl(authenticatedUser.getUsername(), "profile") + .getPresignedUrl() + ); + + return this.generateUserWithTokens( + authenticatedUser, + request + ); + + } + + @Override + public void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException { + user.setProfilePictureUrl( + this.storageService.getFileUrl(user.getUsername(), "profile") + .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, @@ -167,63 +195,35 @@ public class AuthServiceImpl implements AuthService { new BadRequestException("Invalid Refresh Token. Please authenticate first.") ); } - return null; - } - @Override - public UserDTO signupUser(@Valid UserDTO user, HttpServletRequest request) { + Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes()); - user.setProvider(Provider.LOCAL); + JWTVerifier verifier = JWT.require(algorithm).build(); + DecodedJWT decodedJWT = verifier.verify(refreshToken); - UserDTO authenticatedUser = this.generateUserWithTokens( - this.userService.saveUser(user), - request - ); - - authenticatedUser.setProfilePictureUrl( - this.storageService.getFileUrl(authenticatedUser.getUsername(), "profile") + UserDTO user = this.userService.getUser(decodedJWT.getSubject()); + user.setProfilePictureUrl( + this.storageService.getFileUrl(user.getUsername(), "profile") .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 void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException { - - UserDTO authenticatedUser = this.generateUserWithTokens( - user, - request - ); - - authenticatedUser.setProfilePictureUrl( - this.storageService.getFileUrl(authenticatedUser.getUsername(), "profile") - .getPresignedUrl() - ); - - response.setContentType(APPLICATION_JSON_VALUE); - new ObjectMapper() - .writeValue(response.getOutputStream(), authenticatedUser); - } - - @Override - public UserDTO generateUserWithTokens(UserDTO user, HttpServletRequest request) { - - Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes()); - - HashMap tokens = this.generateTokens(user, algorithm, request); - - HttpSession httpSession = request.getSession(); - UserDTO userAuthenticated = user.toResponse(tokens.get("accessToken"), tokens.get("refreshToken")); - - httpSession.setAttribute("user", userAuthenticated); - - return userAuthenticated; - } - - @Override - public UserDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) { + public AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) { if (Objects.nonNull(user.getId())) { this.userService.alterUser(user.getId(), user); @@ -234,25 +234,49 @@ public class AuthServiceImpl implements AuthService { return this.generateUserWithTokens(user, request); } + @Override public void loginOAuthUser(HttpServletRequest request, - HttpServletResponse response, - OAuth2User oauthUser) throws IOException { + HttpServletResponse response, + OAuth2User oauthUser) throws IOException { - String[] url = request.getRequestURL().toString().split("/"); - String clientId = url[url.length-1]; + String clientId = this.getClientFromUrl(request.getRequestURL().toString()); - OAuthMap oauthMap = null; + 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 { - oauthMap = (OAuthMap) OAuthMapper.byValue(clientId).getMap() + return (OAuthMap) OAuthMapper.byValue(clientId).getMap() .getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser); } catch (Exception e) { - throw new BadRequestException("No Such Provider"); + throw new BadRequestException("Unsupported OAuth Client."); } + } + private UserDTO generateUserFromAuthUser(OAuthMap oauthMap, OAuth2User oauthUser) { UserDTO user = null; try { user = this.userService.getUser(oauthMap.getPrincipal()); - user.setProfilePictureUrl(oauthMap.getProfilePicture()); } catch (BadRequestException e) { user = UserDTO.builder() .name(oauthUser.getAttribute("name")) @@ -260,24 +284,11 @@ public class AuthServiceImpl implements AuthService { .email(oauthUser.getAttribute("email")) .roles(Arrays.asList(Role.USER)) .provider(oauthMap.getProvider()) - .profilePictureUrl(oauthMap.getProfilePicture()) .build(); } + user.setProfilePictureUrl(oauthMap.getProfilePicture()); - UserDTO authenticatedUser = this.processOAuthPostLogin( - user, - request - ); - - response.setContentType(APPLICATION_JSON_VALUE); - new ObjectMapper() - .writeValue(response.getOutputStream(), authenticatedUser); - - } - - public UserDTO getLoggedUser() { - String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - return userService.getUser(username); + return user; } } diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/session/api/SessionController.java b/src/main/java/com/hideyoshi/backendportfolio/base/session/api/SessionController.java index 580c864..f856a80 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/session/api/SessionController.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/session/api/SessionController.java @@ -1,5 +1,6 @@ package com.hideyoshi.backendportfolio.base.session.api; +import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import com.hideyoshi.backendportfolio.base.session.service.SessionManagerService; import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import lombok.RequiredArgsConstructor; @@ -19,7 +20,7 @@ public class SessionController { private final SessionManagerService sessionManagerService; @GetMapping(path = "/validate") - public ResponseEntity validateCurrentSession(HttpSession session) { + public ResponseEntity validateCurrentSession(HttpSession session) { return ResponseEntity.ok(this.sessionManagerService.validateSession(session)); } diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerService.java b/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerService.java index 6136149..add7bac 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerService.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerService.java @@ -1,12 +1,12 @@ package com.hideyoshi.backendportfolio.base.session.service; -import com.hideyoshi.backendportfolio.base.user.model.UserDTO; +import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import javax.servlet.http.HttpSession; public interface SessionManagerService { - UserDTO validateSession(HttpSession session); + AuthDTO validateSession(HttpSession session); void destroySession(HttpSession session); diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerServiceImpl.java b/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerServiceImpl.java index d66f29d..c21ee03 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerServiceImpl.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/session/service/SessionManagerServiceImpl.java @@ -1,32 +1,19 @@ package com.hideyoshi.backendportfolio.base.session.service; -import com.hideyoshi.backendportfolio.base.user.model.UserDTO; +import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import com.hideyoshi.backendportfolio.base.user.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import javax.servlet.http.HttpSession; -import java.util.Objects; @Service @RequiredArgsConstructor public class SessionManagerServiceImpl implements SessionManagerService { - private final UserService userService; - @Override - public UserDTO validateSession(HttpSession session) { - - UserDTO sessionObject = (UserDTO) session.getAttribute("user"); - - if (Objects.nonNull(sessionObject)) { - sessionObject = sessionObject.toResponse( - sessionObject.getAccessToken(), - sessionObject.getRefreshToken() - ); - } - - return sessionObject; + public AuthDTO validateSession(HttpSession session) { + return (AuthDTO) session.getAttribute("user"); } @Override diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/user/api/UserController.java b/src/main/java/com/hideyoshi/backendportfolio/base/user/api/UserController.java index 99e85da..e95c29a 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/user/api/UserController.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/user/api/UserController.java @@ -1,5 +1,6 @@ package com.hideyoshi.backendportfolio.base.user.api; +import com.hideyoshi.backendportfolio.base.security.model.AuthDTO; import com.hideyoshi.backendportfolio.base.security.service.AuthService; import com.hideyoshi.backendportfolio.base.user.model.TokenDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO; @@ -45,7 +46,7 @@ public class UserController { @PostMapping("/signup") @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) - public ResponseEntity signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) { + public ResponseEntity signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) { URI uri = URI.create( ServletUriComponentsBuilder .fromCurrentContextPath() @@ -56,7 +57,7 @@ public class UserController { @PostMapping("/login/refresh") @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) - public ResponseEntity refreshAccessToken( + public ResponseEntity refreshAccessToken( @RequestBody @Valid TokenDTO refreshToken, HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/user/model/TokenDTO.java b/src/main/java/com/hideyoshi/backendportfolio/base/user/model/TokenDTO.java index c293e12..e290f56 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/user/model/TokenDTO.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/user/model/TokenDTO.java @@ -1,6 +1,8 @@ package com.hideyoshi.backendportfolio.base.user.model; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.*; import javax.validation.constraints.NotNull; @@ -10,6 +12,8 @@ import java.util.Date; @Data @NoArgsConstructor @AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) public class TokenDTO implements Serializable { @NotNull(message = "Invalid AccessToken. Please Authenticate first.") diff --git a/src/main/java/com/hideyoshi/backendportfolio/base/user/model/UserDTO.java b/src/main/java/com/hideyoshi/backendportfolio/base/user/model/UserDTO.java index 5bfdfa4..9e4ce86 100644 --- a/src/main/java/com/hideyoshi/backendportfolio/base/user/model/UserDTO.java +++ b/src/main/java/com/hideyoshi/backendportfolio/base/user/model/UserDTO.java @@ -55,10 +55,6 @@ public class UserDTO implements UserDetails { private String profilePictureUrl; - private TokenDTO accessToken; - - private TokenDTO refreshToken; - private Provider provider; public UserDTO(User entity) { @@ -118,26 +114,13 @@ public class UserDTO implements UserDetails { } public UserDTO toResponse() { - return UserDTO.builder() - .name(this.name) - .email(this.email) - .username(this.username) - .provider(this.provider) - .profilePictureUrl(this.profilePictureUrl) - .build(); - } - - public UserDTO toResponse(TokenDTO accessToken, TokenDTO refreshToken) { return UserDTO.builder() .id(this.id) .name(this.name) .email(this.email) .username(this.username) .provider(this.provider) - .roles(this.roles) .profilePictureUrl(this.profilePictureUrl) - .accessToken(accessToken) - .refreshToken(refreshToken) .build(); }