중간프로젝트 JWT, 로그인 기능

우정·2023년 1월 3일
0

작업한 내용

JWT

  • build.gradle
	compileOnly group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2'
	runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2'
	runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2'
  • application.properties
jwt.secret.key=7ZWt7ZW0OTntmZTsnbTtjIXtlZzqta3snYTrhIjrqLjshLjqs4TroZzrgpjslYTqsIDsnpDtm4zrpa3tlZzqsJzrsJzsnpDrpbzrp4zrk6TslrTqsIDsnpA=
  • JwtUtil
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {

    // Header KEY 값
    public static final String AUTHORIZATION_HEADER = "Authorization";
    // 사용자 권한 값의 KEY
    public static final String AUTHORIZATION_KEY = "auth";
    // Token 식별자
    private static final String BEARER_PREFIX = "Bearer ";
    // 토큰 만료시간
    private static final long TOKEN_TIME = 60 * 60 * 1000L;

    @Value("${jwt.secret.key}")
    private String secretKey;
    private Key key;
    private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

    @PostConstruct
    public void init() {
        byte[] bytes = Base64.getDecoder().decode(secretKey);
        key = Keys.hmacShaKeyFor(bytes);
    }

    // header 토큰을 가져오기
    public String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
            return bearerToken.substring(7);
        }
        return null;
    }

    // 토큰 생성
    public String createToken(String username, UserRoleEnum role) {
        Date date = new Date();

        return BEARER_PREFIX +
                Jwts.builder()
                        .setSubject(username)
                        .claim(AUTHORIZATION_KEY, role)
                        .setExpiration(new Date(date.getTime() + TOKEN_TIME))
                        .setIssuedAt(date)
                        .signWith(key, signatureAlgorithm)
                        .compact();
    }

    // 토큰 검증
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (SecurityException | MalformedJwtException e) {
            log.info("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
        } catch (ExpiredJwtException e) {
            log.info("Expired JWT token, 만료된 JWT token 입니다.");
        } catch (UnsupportedJwtException e) {
            log.info("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
        } catch (IllegalArgumentException e) {
            log.info("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
        }
        return false;
    }

    // 토큰에서 사용자 정보 가져오기
    public Claims getUserInfoFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
    }

}

아직 뭔 소리인지,, 검증 로직 작성해야하는데 이건 또 뭔지,, 공부를 좀 더 해야할 듯 ㅜ

로그인

  • UserService
@Transactional(readOnly = true)
    public String loginUser(LoginUserRequest loginUserRequest) {
        String username = loginUserRequest.getUsername();
        String password = loginUserRequest.getPassword();

        User user = userRepository.findByUsername(username).orElseThrow(
                () -> new IllegalArgumentException("등록된 사용자가 없습니다.")
        );

        if (!user.getPassword().equals(password)) {
            throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
        }

        String generatedToken = jwtUtil.createToken(user.getUsername(), user.getRole());
        return generatedToken;
    }
  • UserController
@PostMapping("/api/login")
    public LoginUserResponse loginUser(@RequestBody LoginUserRequest loginUserRequest, HttpServletResponse response) {
        String generatedToken = userService.loginUser(loginUserRequest);
        response.addHeader(JwtUtil.AUTHORIZATION_HEADER, generatedToken);
        return new LoginUserResponse(200L, "로그인 완료");
    }
  • LoginUserRequest
@Getter
public class LoginUserRequest {
    private String username;
    private String password;
}
  • LoginUserResponse
@Getter
public class LoginUserResponse {
    private final Long statusCode;
    private final String statusMessage;

    public LoginUserResponse(Long statusCode, String statusMessage) {
        this.statusCode = statusCode;
        this.statusMessage = statusMessage;
    }
}

피드백
패스워드 검증은 패스워드를 저장하고있는 엔티티의 역할
따라서, 엔티티 내부에 커스텀 메서드를 작성하고, 거기에서 검증을 하는게 역할과 책임 측면에서 더 맞음

->

  • User
    public boolean isValidPassword(String password) {
        return this.password.equals(password);
    }
  • UserService
if (!user.isValidPassword(loginUserRequest.getPassword())) {
            throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
        }

테스트 완료! 작동 완료!

그리고 ADMIN으로 회원가입 했을 때 확인을 어떻게 해야하는지 모르겠음,, 토큰으로 하는 거 맞나/..? 내일 이거에 대해서 조금 더 공부해보도록 하자

0개의 댓글

관련 채용 정보