Implements New AuthDTO and Refactors AuthService Implementation

This commit is contained in:
2023-08-22 01:05:21 -03:00
parent 68721381a6
commit 6dbd6452fe
9 changed files with 170 additions and 148 deletions

View File

@@ -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;
}
}

View File

@@ -1,6 +1,7 @@
package com.hideyoshi.backendportfolio.base.security.service; package com.hideyoshi.backendportfolio.base.security.service;
import com.auth0.jwt.algorithms.Algorithm; 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.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -22,13 +23,13 @@ public interface AuthService {
UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader); 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; void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException;

View File

@@ -5,6 +5,7 @@ import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper; 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.OAuthMap;
import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMapper; import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.backendportfolio.base.user.entity.Provider;
@@ -102,7 +103,7 @@ public class AuthServiceImpl implements AuthService {
TokenDTO accessToken = generateAccessToken(user, algorithm, request); TokenDTO accessToken = generateAccessToken(user, algorithm, request);
TokenDTO refreshToken = generateRefreshToken(user, algorithm, request); TokenDTO refreshToken = generateRefreshToken(user, algorithm, request);
HashMap<String,TokenDTO> tokens = new HashMap<>(); HashMap<String, TokenDTO> tokens = new HashMap<>();
tokens.put("accessToken", accessToken); tokens.put("accessToken", accessToken);
tokens.put("refreshToken", refreshToken); tokens.put("refreshToken", refreshToken);
@@ -112,54 +113,81 @@ public class AuthServiceImpl implements AuthService {
@Override @Override
public UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader) { public UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader) {
if (authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) { 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);
} }
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 @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<String, TokenDTO> tokens = this.generateTokens(user, algorithm, request);
JWTVerifier verifier = JWT.require(algorithm).build(); HttpSession httpSession = request.getSession();
DecodedJWT decodedJWT = verifier.verify(refreshToken); 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(); @Override
UserDTO authenticatedUser = user.toResponse( public AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request) {
this.generateAccessToken(user, algorithm, request),
new TokenDTO(
refreshToken,
decodedJWT.getExpiresAt()
)
);
httpSession.setAttribute("user", authenticatedUser);
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( resolver.resolveException(
request, request,
response, response,
@@ -167,63 +195,35 @@ public class AuthServiceImpl implements AuthService {
new BadRequestException("Invalid Refresh Token. Please authenticate first.") new BadRequestException("Invalid Refresh Token. Please authenticate first.")
); );
} }
return null;
}
@Override Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
public UserDTO signupUser(@Valid UserDTO user, HttpServletRequest request) {
user.setProvider(Provider.LOCAL); JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(refreshToken);
UserDTO authenticatedUser = this.generateUserWithTokens( UserDTO user = this.userService.getUser(decodedJWT.getSubject());
this.userService.saveUser(user), user.setProfilePictureUrl(
request this.storageService.getFileUrl(user.getUsername(), "profile")
);
authenticatedUser.setProfilePictureUrl(
this.storageService.getFileUrl(authenticatedUser.getUsername(), "profile")
.getPresignedUrl() .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; return authenticatedUser;
} }
@Override @Override
public void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException { public AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) {
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<String, TokenDTO> 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) {
if (Objects.nonNull(user.getId())) { if (Objects.nonNull(user.getId())) {
this.userService.alterUser(user.getId(), user); this.userService.alterUser(user.getId(), user);
@@ -234,25 +234,49 @@ public class AuthServiceImpl implements AuthService {
return this.generateUserWithTokens(user, request); return this.generateUserWithTokens(user, request);
} }
@Override
public void loginOAuthUser(HttpServletRequest request, public void loginOAuthUser(HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
OAuth2User oauthUser) throws IOException { OAuth2User oauthUser) throws IOException {
String[] url = request.getRequestURL().toString().split("/"); String clientId = this.getClientFromUrl(request.getRequestURL().toString());
String clientId = url[url.length-1];
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 { try {
oauthMap = (OAuthMap) OAuthMapper.byValue(clientId).getMap() return (OAuthMap) OAuthMapper.byValue(clientId).getMap()
.getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser); .getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser);
} catch (Exception e) { } 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; UserDTO user = null;
try { try {
user = this.userService.getUser(oauthMap.getPrincipal()); user = this.userService.getUser(oauthMap.getPrincipal());
user.setProfilePictureUrl(oauthMap.getProfilePicture());
} catch (BadRequestException e) { } catch (BadRequestException e) {
user = UserDTO.builder() user = UserDTO.builder()
.name(oauthUser.getAttribute("name")) .name(oauthUser.getAttribute("name"))
@@ -260,24 +284,11 @@ public class AuthServiceImpl implements AuthService {
.email(oauthUser.getAttribute("email")) .email(oauthUser.getAttribute("email"))
.roles(Arrays.asList(Role.USER)) .roles(Arrays.asList(Role.USER))
.provider(oauthMap.getProvider()) .provider(oauthMap.getProvider())
.profilePictureUrl(oauthMap.getProfilePicture())
.build(); .build();
} }
user.setProfilePictureUrl(oauthMap.getProfilePicture());
UserDTO authenticatedUser = this.processOAuthPostLogin( return user;
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);
} }
} }

View File

@@ -1,5 +1,6 @@
package com.hideyoshi.backendportfolio.base.session.api; 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.session.service.SessionManagerService;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -19,7 +20,7 @@ public class SessionController {
private final SessionManagerService sessionManagerService; private final SessionManagerService sessionManagerService;
@GetMapping(path = "/validate") @GetMapping(path = "/validate")
public ResponseEntity<UserDTO> validateCurrentSession(HttpSession session) { public ResponseEntity<AuthDTO> validateCurrentSession(HttpSession session) {
return ResponseEntity.ok(this.sessionManagerService.validateSession(session)); return ResponseEntity.ok(this.sessionManagerService.validateSession(session));
} }

View File

@@ -1,12 +1,12 @@
package com.hideyoshi.backendportfolio.base.session.service; 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; import javax.servlet.http.HttpSession;
public interface SessionManagerService { public interface SessionManagerService {
UserDTO validateSession(HttpSession session); AuthDTO validateSession(HttpSession session);
void destroySession(HttpSession session); void destroySession(HttpSession session);

View File

@@ -1,32 +1,19 @@
package com.hideyoshi.backendportfolio.base.session.service; 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 com.hideyoshi.backendportfolio.base.user.service.UserService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.util.Objects;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class SessionManagerServiceImpl implements SessionManagerService { public class SessionManagerServiceImpl implements SessionManagerService {
private final UserService userService;
@Override @Override
public UserDTO validateSession(HttpSession session) { public AuthDTO validateSession(HttpSession session) {
return (AuthDTO) session.getAttribute("user");
UserDTO sessionObject = (UserDTO) session.getAttribute("user");
if (Objects.nonNull(sessionObject)) {
sessionObject = sessionObject.toResponse(
sessionObject.getAccessToken(),
sessionObject.getRefreshToken()
);
}
return sessionObject;
} }
@Override @Override

View File

@@ -1,5 +1,6 @@
package com.hideyoshi.backendportfolio.base.user.api; 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.security.service.AuthService;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO; import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
@@ -45,7 +46,7 @@ public class UserController {
@PostMapping("/signup") @PostMapping("/signup")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<UserDTO> signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) { public ResponseEntity<AuthDTO> signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) {
URI uri = URI.create( URI uri = URI.create(
ServletUriComponentsBuilder ServletUriComponentsBuilder
.fromCurrentContextPath() .fromCurrentContextPath()
@@ -56,7 +57,7 @@ public class UserController {
@PostMapping("/login/refresh") @PostMapping("/login/refresh")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<UserDTO> refreshAccessToken( public ResponseEntity<AuthDTO> refreshAccessToken(
@RequestBody @Valid TokenDTO refreshToken, @RequestBody @Valid TokenDTO refreshToken,
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response) { HttpServletResponse response) {

View File

@@ -1,6 +1,8 @@
package com.hideyoshi.backendportfolio.base.user.model; package com.hideyoshi.backendportfolio.base.user.model;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.*; import lombok.*;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -10,6 +12,8 @@ import java.util.Date;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TokenDTO implements Serializable { public class TokenDTO implements Serializable {
@NotNull(message = "Invalid AccessToken. Please Authenticate first.") @NotNull(message = "Invalid AccessToken. Please Authenticate first.")

View File

@@ -55,10 +55,6 @@ public class UserDTO implements UserDetails {
private String profilePictureUrl; private String profilePictureUrl;
private TokenDTO accessToken;
private TokenDTO refreshToken;
private Provider provider; private Provider provider;
public UserDTO(User entity) { public UserDTO(User entity) {
@@ -118,26 +114,13 @@ public class UserDTO implements UserDetails {
} }
public UserDTO toResponse() { 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() return UserDTO.builder()
.id(this.id) .id(this.id)
.name(this.name) .name(this.name)
.email(this.email) .email(this.email)
.username(this.username) .username(this.username)
.provider(this.provider) .provider(this.provider)
.roles(this.roles)
.profilePictureUrl(this.profilePictureUrl) .profilePictureUrl(this.profilePictureUrl)
.accessToken(accessToken)
.refreshToken(refreshToken)
.build(); .build();
} }