[code]
package api
import (
“fmt”
“net/http”
“os”
“time”
“github.com/dgrijalva/jwt-go”
“github.com/labstack/echo”
g “githumb.com/hyuk-june/petoo_back_end/app/globals”
“githumb.com/hyuk-june/petoo_back_end/app/models”
)
type AuthApi struct {
}
// Login 성공하면 access 토큰과 refresh 토큰을 출력한다
func (a *AuthApi) Login(c echo.Context) error {
email := c.FormValue(“email”)
password := c.FormValue(“password”)
lu := &models.LoginUser{
Email: email,
Password: password,
}
// Validate
if err := c.Validate(lu); err != nil {
return c.JSON(http.StatusBadRequest, err.Error())
}
// database 로부터 해당 사용자정보를 불러온다
user := new(models.User)
if err := g.DB.First(user, “email=?”, email).Error; err != nil {
return echo.ErrUnauthorized
}
// 비밀번호를 검증한다
if err := models.VerifyPassword(user.Password, password); err != nil {
fmt.Println(err)
fmt.Println(err.Error())
return echo.ErrUnauthorized
}
// claims(데이터) 생성
claims := map[string]interface{}{
“sub”: os.Getenv(“API_TOKEN_SUB”),
“id”: user.ID,
“email”: user.Email,
}
// access, refresh 토큰 생성
accessToken, refreshToken, err := a.generateToken(c, claims)
if err != nil {
return err
}
// 출력
return c.JSON(http.StatusOK, map[string]string{
“access_token”: accessToken,
“refresh_token”: refreshToken,
})
}
// RefreshToken 토큰갱신: 성공하면 access 토큰과 refresh 토큰을 모두 다시 출력한다.
func (a *AuthApi) RefreshToken(c echo.Context) error {
refreshToken := c.FormValue(“refresh_token”)
// 토큰을 분석하고 유효한지 검증하고 *jwt.Token 객체로 반환한다.
// KeyFunc 은 sceret key를 반환해야 한다.
token, err := jwt.Parse(refreshToken, func(token *jwt.Token) (interface{}, error) {
// 클라이언트로 받은 토큰이 HMAC 알고리즘이 맞는지 확인
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf(“Unexpected signing method: %v”, token.Header[“alg”])
}
return []byte(os.Getenv(“API_SECRET”)), nil
})
if err != nil {
return err
}
// Claims 생성
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid {
return echo.ErrInternalServerError
}
// 발급할때의 토큰제목과 일치하는지 체크
if claims[“sub”].(string) != os.Getenv(“API_TOKEN_SUB”) {
return echo.ErrBadRequest
}
// 재발급
cl := map[string]interface{}(claims)
accessToken, refreshToken, err := a.generateToken(c, cl)
if err != nil {
return err
}
// 출력
return c.JSON(http.StatusOK, map[string]string{
“access_token”: accessToken,
“refresh_token”: refreshToken,
})
}
// generateToken access 토큰과 refresh 토큰을 쌍으로 반환한다
func (a *AuthApi) generateToken(c echo.Context, claimsMap map[string]interface{}) (string, string, error) {
// access 토큰 생성: 유효기간 20분
accessToken, err := a.createToken(
c,
claimsMap,
time.Now().Add(time.Minute*20),
“access_token”,
)
if err != nil {
return “”, “”, err
}
// refresh 토큰 생성: 유효기간 24시간
refreshToken, err := a.createToken(
c,
claimsMap,
time.Now().Add(time.Hour*24),
“refresh_token”,
)
if err != nil {
return “”, “”, err
}
return accessToken, refreshToken, nil
}
// 단위 토큰 만들기
func (a *AuthApi) createToken(c echo.Context, data map[string]interface{}, expire time.Time, cookieName string) (string, error) {
// token
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
for key, val := range data {
claims[key] = val
}
claims[“exp”] = expire.Unix()
encToken, err := token.SignedString([]byte(os.Getenv(“API_SECRET”)))
if err != nil {
return “”, err
}
// 쿠키 (클라이언트가 따로 저장하는 수고를 덜기 위해서…) – Option
if cookieName != “” {
cookie := new(http.Cookie)
cookie.Name = cookieName
cookie.Value = encToken
cookie.Expires = expire
c.SetCookie(cookie)
}
return encToken, nil
}
[/code]