-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #81 from duohui12/duohui12
6주차 과제 - 로그인 만들기
- Loading branch information
Showing
19 changed files
with
631 additions
and
15,804 deletions.
There are no files selected for viewing
48 changes: 48 additions & 0 deletions
48
app/src/main/java/com/codesoom/assignment/application/AuthenticationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.codesoom.assignment.application; | ||
|
||
import com.codesoom.assignment.domain.User; | ||
import com.codesoom.assignment.domain.UserRepository; | ||
import com.codesoom.assignment.dto.UserLoginData; | ||
import com.codesoom.assignment.errors.InvalidAccessTokenException; | ||
import com.codesoom.assignment.errors.LoginFailException; | ||
import com.codesoom.assignment.errors.UserNotFoundException; | ||
import com.codesoom.assignment.utils.JwtUtil; | ||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.SignatureException; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class AuthenticationService { | ||
|
||
private final JwtUtil jwtUtil; | ||
private final UserRepository userRepository; | ||
|
||
public AuthenticationService(JwtUtil jwtUtil,UserRepository userRepository){ | ||
this.jwtUtil = jwtUtil; | ||
this.userRepository = userRepository; | ||
} | ||
|
||
public String login(UserLoginData userLoginData){ | ||
User user = userRepository.findByEmail(userLoginData.getEmail()).orElseThrow(() -> new UserNotFoundException()); | ||
|
||
if(!user.getPassword().equals(userLoginData.getPassword())){ | ||
throw new LoginFailException(); | ||
} | ||
|
||
return jwtUtil.encode(user.getId()); | ||
} | ||
|
||
public Long parseToken(String accessToken) { | ||
if(accessToken == null || accessToken.isBlank()){ | ||
throw new InvalidAccessTokenException(); | ||
} | ||
|
||
try{ | ||
Claims claims = jwtUtil.decode(accessToken); | ||
return claims.get("userId", Long.class); | ||
}catch (SignatureException e){ | ||
throw new InvalidAccessTokenException(); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
app/src/main/java/com/codesoom/assignment/config/WebConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.codesoom.assignment.config; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
@Configuration | ||
public class WebConfig implements WebMvcConfigurer { | ||
|
||
private HandlerInterceptor handlerInterceptor; | ||
|
||
public WebConfig(HandlerInterceptor handlerInterceptor) { | ||
this.handlerInterceptor = handlerInterceptor; | ||
} | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { | ||
registry.addInterceptor(handlerInterceptor); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
app/src/main/java/com/codesoom/assignment/controllers/SessionController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.codesoom.assignment.controllers; | ||
|
||
import com.codesoom.assignment.application.AuthenticationService; | ||
import com.codesoom.assignment.dto.SessionResponseData; | ||
import com.codesoom.assignment.dto.UserLoginData; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.*; | ||
import javax.validation.Valid; | ||
|
||
@RestController | ||
@RequestMapping("/session") | ||
@CrossOrigin | ||
public class SessionController { | ||
|
||
private final AuthenticationService authenticationService; | ||
|
||
public SessionController(AuthenticationService authenticationService) { | ||
this.authenticationService = authenticationService; | ||
} | ||
|
||
@PostMapping | ||
@ResponseStatus(HttpStatus.CREATED) | ||
public SessionResponseData login(@RequestBody @Valid UserLoginData userLoginData){ | ||
String accessToken = authenticationService.login(userLoginData); | ||
|
||
return SessionResponseData.builder() | ||
.accessToken(accessToken) | ||
.build(); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
app/src/main/java/com/codesoom/assignment/dto/SessionResponseData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.codesoom.assignment.dto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@Builder | ||
@AllArgsConstructor | ||
public class SessionResponseData { | ||
|
||
private String accessToken; | ||
} |
23 changes: 23 additions & 0 deletions
23
app/src/main/java/com/codesoom/assignment/dto/UserLoginData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.codesoom.assignment.dto; | ||
|
||
import com.github.dozermapper.core.Mapping; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.checkerframework.checker.units.qual.A; | ||
|
||
import javax.validation.constraints.NotBlank; | ||
import javax.validation.constraints.Size; | ||
|
||
@Getter | ||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class UserLoginData { | ||
@NotBlank | ||
private String email; | ||
|
||
@NotBlank | ||
private String password; | ||
} |
8 changes: 8 additions & 0 deletions
8
app/src/main/java/com/codesoom/assignment/errors/InvalidAccessTokenException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.codesoom.assignment.errors; | ||
|
||
public class InvalidAccessTokenException extends RuntimeException{ | ||
public InvalidAccessTokenException() { | ||
super("Invalid access token" ); | ||
} | ||
|
||
} |
8 changes: 8 additions & 0 deletions
8
app/src/main/java/com/codesoom/assignment/errors/LoginFailException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.codesoom.assignment.errors; | ||
|
||
public class LoginFailException extends RuntimeException{ | ||
public LoginFailException() { | ||
super("Login Fail"); | ||
} | ||
|
||
} |
4 changes: 2 additions & 2 deletions
4
app/src/main/java/com/codesoom/assignment/errors/UserNotFoundException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
package com.codesoom.assignment.errors; | ||
|
||
public class UserNotFoundException extends RuntimeException { | ||
public UserNotFoundException(Long id) { | ||
super("User not found: " + id); | ||
public UserNotFoundException() { | ||
super("User not found"); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
app/src/main/java/com/codesoom/assignment/interceptors/AuthenticationInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.codesoom.assignment.interceptors; | ||
|
||
import com.codesoom.assignment.application.AuthenticationService; | ||
import com.codesoom.assignment.dto.ErrorResponse; | ||
import com.codesoom.assignment.errors.InvalidAccessTokenException; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.ResponseStatus; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
@Component | ||
public class AuthenticationInterceptor implements HandlerInterceptor { | ||
|
||
private final AuthenticationService authenticationService; | ||
|
||
public AuthenticationInterceptor(AuthenticationService authenticationService) { | ||
this.authenticationService = authenticationService; | ||
} | ||
|
||
@Override | ||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | ||
String requestURI = request.getRequestURI(); | ||
String requestMethod = request.getMethod(); | ||
|
||
/* | ||
e2e 테스트시 | ||
... blocked by CORS policy: | ||
Response to preflight request doesn't pass access control check: | ||
It does not have HTTP ok status. 오류 발생해서 추가 | ||
*/ | ||
if (requestMethod.equals(HttpMethod.OPTIONS.name())) { | ||
return true; | ||
} | ||
|
||
if(!requestURI.startsWith("/products")){ | ||
return true; | ||
} | ||
|
||
if(requestMethod.equals(HttpMethod.GET.name())){ | ||
return true; | ||
} | ||
|
||
String authorization = request.getHeader("Authorization"); | ||
if(authorization==null) throw new InvalidAccessTokenException(); | ||
String accessToken = authorization.substring("Bearer ".length()); | ||
|
||
Long userId = authenticationService.parseToken(accessToken); | ||
return true; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
app/src/main/java/com/codesoom/assignment/utils/JwtUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.codesoom.assignment.utils; | ||
|
||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.security.Keys; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
import java.security.Key; | ||
|
||
@Component | ||
public class JwtUtil { | ||
private final Key key; | ||
|
||
public JwtUtil(@Value("${jwt.secret}") String secret){ | ||
key = Keys.hmacShaKeyFor(secret.getBytes()); | ||
} | ||
|
||
public String encode(Long userId){ | ||
return Jwts.builder() | ||
.claim("userId",userId) | ||
.signWith(key) | ||
.compact(); | ||
} | ||
|
||
public Claims decode(String accessToken){ | ||
return Jwts.parserBuilder() | ||
.setSigningKey(key) | ||
.build() | ||
.parseClaimsJws(accessToken) | ||
.getBody(); | ||
} | ||
|
||
} |
84 changes: 84 additions & 0 deletions
84
app/src/test/java/com/codesoom/assignment/application/AuthenticationServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package com.codesoom.assignment.application; | ||
|
||
import com.codesoom.assignment.domain.User; | ||
import com.codesoom.assignment.domain.UserRepository; | ||
import com.codesoom.assignment.dto.UserLoginData; | ||
import com.codesoom.assignment.errors.InvalidAccessTokenException; | ||
import com.codesoom.assignment.errors.LoginFailException; | ||
import com.codesoom.assignment.utils.JwtUtil; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import java.util.Optional; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.mockito.BDDMockito.given; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.verify; | ||
|
||
class AuthenticationServiceTest { | ||
|
||
private static final String SECRET = "12345678901234567890123456789010"; | ||
private AuthenticationService authenticationService; | ||
private final UserRepository userRepository = mock(UserRepository.class); | ||
private final JwtUtil jwtUtil = new JwtUtil(SECRET); | ||
|
||
private static final String VALID_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9.neCsyNLzy3lQ4o2yliotWT06FwSGZagaHpKdAkjnGGw"; | ||
private static final String INVALID_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9.neCsyNLzy3lQ4o2yliotWT06FwSGZagaHpKdAkjnGKK"; | ||
public static final String VALID_EMAIL = "[email protected]"; | ||
private static final String VALID_PASSWORD = "1111"; | ||
public static final String INVALID_EMAIL = "dh"; | ||
private static final String INVALID_PASSWORD = "0"; | ||
private static final UserLoginData VALID_LOGIN_DATA = UserLoginData.builder() | ||
.email(VALID_EMAIL) | ||
.password(VALID_PASSWORD) | ||
.build(); | ||
private static final UserLoginData INVALID_LOGIN_DATA = UserLoginData.builder() | ||
.email(INVALID_EMAIL) | ||
.password(INVALID_PASSWORD) | ||
.build(); | ||
|
||
@BeforeEach | ||
void setup(){ | ||
authenticationService = new AuthenticationService(jwtUtil,userRepository); | ||
} | ||
|
||
@Test | ||
void loginWithValidLoginData(){ | ||
given(userRepository.findByEmail(VALID_EMAIL)) | ||
.willReturn(Optional.of( User.builder() | ||
.id(1L) | ||
.email(VALID_EMAIL) | ||
.password(VALID_PASSWORD) | ||
.build())); | ||
|
||
String accessToken = authenticationService.login(VALID_LOGIN_DATA); | ||
|
||
assertThat(accessToken).isEqualTo(VALID_TOKEN); | ||
verify(userRepository).findByEmail(VALID_EMAIL); | ||
} | ||
|
||
@Test | ||
void loginWithInvalidLoginData(){ | ||
given(userRepository.findByEmail(INVALID_EMAIL)) | ||
.willThrow(new LoginFailException()); | ||
|
||
assertThatThrownBy(() -> authenticationService.login(INVALID_LOGIN_DATA)) | ||
.isInstanceOf(LoginFailException.class); | ||
} | ||
|
||
@Test | ||
void parseTokenWithValidToken() { | ||
Long id = authenticationService.parseToken(VALID_TOKEN); | ||
|
||
assertThat(id).isEqualTo(1L); | ||
} | ||
|
||
@Test | ||
void parseTokenWithInvalidToken() { | ||
assertThatThrownBy(() -> authenticationService.parseToken("")) | ||
.isInstanceOf(InvalidAccessTokenException.class); | ||
|
||
assertThatThrownBy(() -> authenticationService.parseToken(INVALID_TOKEN)) | ||
.isInstanceOf(InvalidAccessTokenException.class); | ||
} | ||
} |
Oops, something went wrong.