이전 글
토큰을 만들고 검증할 수 있게 되었으니 다음으로 인증 미들웨어를 구현해 보자.
인증 미들웨어 구현
리퀘스트 헤더에서 토큰 값을 가져오는 getTokenFromRequest 메서드부터 구현해 보자.
인증 방식이 Basic 이 아닌 Bearer 인지 확인한 후 토큰을 가져오는 메서드로,
인증 방식 스킴은 대소문자 구분이 없으므로 인증 방식 스킴 비교 시에는 대문자로 통일해서 검증하며,
헤더 값에서 스킴은 빼고 토큰 값만 반환한다.
const (
BearerSchema string = "BEARER "
)
func getTokenFromRequest(r *http.Request) (string, error) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
return "", errors.New(authentication.ErrAuthorizationHeaderRequired)
}
bearerLength := len(BearerSchema)
if len(authHeader) > bearerLength && strings.ToUpper(authHeader[0:bearerLength]) == BearerSchema {
return authHeader[bearerLength:], nil
}
return "", errors.New(authentication.ErrInvalidBearerScheme)
}
다음으로 인증 미들웨어 함수를 만들어 반환하는 AuthenticateMiddleware 메서드를 구현해 보자.
인증 미들웨어를 반환하는 부분은 Authentication 구조체를 만들어서 구조체의 함수로 변경하였다.
리퀘스트 헤더에서 토큰 가져와서 검증하고, 검증이 통과되면 context 에 저장하고
다음 http Handler 에게 넘겨준다. 요청 전 단계에서 사용되는 스프링의 인터셉터 같은 느낌이다.
type Authentication struct {
secret string
}
func (a *Authentication) AuthenticateMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := getTokenFromRequest(r)
if err != nil {
http.Error(w, err.Error(), authentication.ErrStatusCode(err))
return
}
userID, err := ValidateToken(token, a.secret)
if err != nil {
http.Error(w, err.Error(), authentication.ErrStatusCode(err))
return
}
ctx := context.WithValue(r.Context(), "user_id", userID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
테스트 코드 작성
어떻게 해도 테스트 하기 힘든 코드는 생산되기 때문에
100% 테스트 커버리지는 신의 영역이라 생각한다.
인증 미들웨어를 반환하는 AuthenticateMiddleware 를 테스트하는 방법 중 하나로
반환되는 함수를 실행시켜 테스트해보는 것이다.
먼저 성공 케이스를 생각해 보면
context 에 User ID 값이 제대로 설정되었나 검증해 볼 수 있을 것이다.
문제는 메서드 안에서 context 가 생성되어 next.ServerHTTP 메서드의 인자 값으로 들어간다.
그러면 next.ServerHTTP 가 실행될 때의 리퀘스트 값에서 context 를 뽑아서 테스트해봐야 하는데
가능할 것 같지 않다.
방법으로는 가짜 목 서버 띄우고 하면 되겠지만 이럴 경우 보통 단위 테스트를 포기하고
통합 테스트에서 실제로 시나리오를 세워서 테스트 해보는게 더 좋은 것 같다.
테스트 작성이 실제 로직 구현보다 빡시다면 그건 잘못되었다고 생각한다.
물론 아무리 힘들어도 테스트는 빡시고 처절하게 테스트 커버리지 100%를 지향해야 한다는 것도 맞다.
세상을 흑백으로 구분할 필요는 없으니까 각자 알아서 판단하고 프로젝트 성향에 맞춰서 판단하면 될 것 같다.
이제 리포지토리, 서비스, 핸들러 구현과 메인 함수 구현만 남았다. 하나씩 하나씩 기능을 완성해 가자.
전체 소스는 https://github.com/jongwon-hyun/go_auth_jwt
다음 글
'Programming > Go' 카테고리의 다른 글
[GO 인증 구현 with JWT 5] Handler, Main 구현 및 통합 테스트 (0) | 2022.04.15 |
---|---|
[GO 인증 구현 with JWT 4] User Repository, Service 구현 및 테스트 (0) | 2022.04.13 |
[GO 인증 구현 with JWT 2] 토큰 구현 및 테스트 코드 작성 (0) | 2022.04.11 |
[GO 인증 구현 with JWT 1] 전체적인 인터페이스 작성 (0) | 2022.04.09 |
Go 로 MinIO 에 파일 업다운로드 (0) | 2022.04.06 |
댓글