[gerrit] Permission denied (publickey) 에러

gerrit에서 clone 받을 때 Permission denied (publickey) 에러가 났을 때 확인해야 할 점 :

1. ssh 접근할 때 username이 gerrit의 'Settings' -> 'Profile' 메뉴 아래에 있는 username과 동일한지 확인

2. 'Settings' -> 'SSH Public Keys'에 public key 등록했는지 확인

3. ssh -vv -p 29418 john.doe@git.example.com 명령어로 접근이 되는지 확인


위와 같이 했는데, 3번에서 아래와 같은 에러가 나는 경우,

debug1: Skipping ssh-dss key /home/<user>/.ssh/id_dsa for not in PubkeyAcceptedKeyTypes


~/.ssh/config 에 파일 생성 후, 아래 설정 추가

PubkeyAcceptedKeyTypes ssh-dss


참고 사이트 :

https://git.eclipse.org/r/Documentation/error-permission-denied.html

https://coderwall.com/p/ykgawg/when-openssh-7-blocks-your-public-key

[Java 1.8] 날짜 정리

Java 1.8 날짜 정리

자바 1.8 이전에는 날짜 연산이 쉽지 않았다. Joda 같은 라이브러리들을 쓰면 된다고 하는데, 필자는 Util 클래스에서 내부적으로 Calendar를 사용하여 연산하고 Date/long/String 간의 변환을 통하여 사용해왔다. 그러던 와중에 자바 1.8의 새로운 날짜들을 보니 신세계가 열렸다. 그래서 간단하게 소개하고 자주 사용될 만한 예제들도 나열하려 한다.

먼저 중요하다고 생각되는 클래스들을 소개하겠다.

클래스

날짜 (Temporal)

Instant : machine time에 유용한 1970년 1월 1일부터 시간을 세는 클래스 (millisecond 뿐만 아니라 nanosecond까지 센다)

LocalDate : [년,월,일]과 같은 날짜만 표현하는 클래스 (시간은 포함하지 않는다)

LocalDateTime : [년,월,일,시,분,초]를 표현하는 클래스 (LocalDate와 함께 가장 많이 쓰이는 클래스가 될 것 같다)

LocalTime : [시,분,초]와 같이 시간만 표현하는 클래스

기간 (TemporalAmount)

Period : 두 날짜 사이의 [년,월,일]로 표현되는 기간 (시간을 다루지 않다 보니 LocalDate를 사용한다)

Duration : 두 시간 사이의 [일,시,분,초]로 표현되는 기간 (Instant 클래스를 사용하고, seconds와 nanoseconds로 측정 되지만 [일,시,분,초]로 변환해 주는 메쏘드를 제공)

기타

ChronoUnit : 한가지의 단위를 표현하기 위한 클래스 (년,월,일,시,분,초 등)

DayOfWeek : 요일

자주 쓰는 메쏘드들

  • 날짜 가져오기

      LocalDate.now(); // 오늘
      LocalDateTime.now(); // 지금
      LocalDate.of(2015, 4, 17); // 2015년4월17일
      LocalDateTime.of(2015, 4, 17, 23, 23, 50); // 2015년4월17일23시23분50초
      Year.of(2015).atMonth(3).atDay(4).atTime(10, 30); // 2015년3월4일 10시30분00초
    
  • 기간 가져오기

      Period.ofYears(2); // 2년간(P2Y)
      Period.ofMonths(5); // 5개월간(P5M)
      Period.ofWeeks(3); // 3주간(P21D)
      Period.ofDays(20); // 20일간(P20D)
    
      Duration.ofDays(2); // 48시간(PT48H)
      Duration.ofHours(8); // 8시간(PT8H)
      Duration.ofMinutes(10); // 10분간(PT10M)
      Duration.ofSeconds(30); // 30초간(PT30S)
    
  • 날짜 + 기간 = 날짜

      LocalTime.of(9, 0, 0).plus(Duration.ofMinutes(10)); // (9시 + 10분간) = 9시10분
      LocalDate.of(2015, 5, 15).plus(Period.ofDays(1)); // (2015년5월15일 + 1일간) = 2015년5월16일
      LocalDateTime.of(2015, 4, 17, 23, 47, 5).minus(Period.ofWeeks(3)); // (2015년4월17일 23시47분05초 - 3주간) = 2015년3월27일 23시47분05초
      LocalDate.now().plusDays(1); // (오늘 + 1일) = 내일
      LocalTime.now().minusHours(3); // (지금 - 3시간) = 3시간 전
    
  • 날짜 - 날짜 = 기간

      Period.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)); // (1953년7월27일 - 1950년6월25일) = 3년1개월2일간(P3Y1M2D)
      Period.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)).getDays(); // 3년1개월2일간 => 2일간
      LocalDate.of(1950, 6, 25).until(LocalDate.of(1953, 7, 27), ChronoUnit.DAYS); // 3년1개월2일간 => 1128일간
      ChronoUnit.DAYS.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)); // 3년1개월2일간 => 1128일간
    
      Duration.between(LocalTime.of(10, 50), LocalTime.of(19, 0)); // (19시00분00초 - 10시50분00초) = 8시간10분간(PT8H10M)
      Duration.between(LocalDateTime.of(2015, 1, 1, 0, 0), LocalDateTime.of(2016, 1, 1, 0, 0)).toDays(); // 365일간
      ChronoUnit.YEARS.between(LocalDate.of(2015, 5, 5), LocalDate.of(2017, 2, 1)); // 1년간
    
  • 날짜 변환하기

    • LocalDate -> String

      LocalDate.of(2020, 12, 12).format(DateTimeFormatter.BASIC_ISO_DATE); // 20201212
      
    • LocalDateTime -> String

      LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2015-04-18 00:42:24
      
    • LocalDateTime -> java.util.Date

      Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()); // Sat Apr 18 01:00:30 KST 2015
      
    • LocalDate -> java.sql.Date

      Date.valueOf(LocalDate.of(2015, 5, 5)); // 2015-05-05
      
    • LocalDateTime -> java.sql.Timestamp

      Timestamp.valueOf(LocalDateTime.now()); // 2015-04-18 01:06:55.323
      
    • String -> LocalDate

      LocalDate.parse("2002-05-09"); // 2002-05-09
      LocalDate.parse("20081004", DateTimeFormatter.BASIC_ISO_DATE); // 2008-10-04
      
    • String -> LocalDateTime

      LocalDateTime.parse("2007-12-03T10:15:30"); // 2007-12-03T10:15:30
      LocalDateTime.parse("2010-11-25 12:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2010-11-25T12:30
      
    • java.util.Date -> LocalDateTime

      LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()); // 2015-04-18T01:16:46.755
      
    • java.sql.Date -> LocalDate

      new Date(System.currentTimeMillis()).toLocalDate(); // 2015-04-18
      
    • java.sql.Timestamp -> LocalDateTime

      new Timestamp(System.currentTimeMillis()).toLocalDateTime(); // 2015-04-18T01:20:07.364
      
    • LocalDateTime -> LocalDate

      LocalDate.from(LocalDateTime.now()); // 2015-04-18
      
    • LocalDate -> LocalDateTime

      LocalDate.now().atTime(2, 30); // 2015-04-18T02:30
      
  • 요일로 날짜 가져오기

      LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SATURDAY)); // 다음 토요일
      LocalDate.of(2016, 5, 1).with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.SUNDAY)); // 2016년5월 세번째 일요일
      LocalDate.of(2015, 7, 1).with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2015년7월 첫번째 월요일
    
  • 언어별 출력

      DayOfWeek.MONDAY.getDisplayName(TextStyle.FULL, Locale.ENGLISH); // Monday
      DayOfWeek.MONDAY.getDisplayName(TextStyle.NARROW, Locale.ENGLISH); // M
      DayOfWeek.MONDAY.getDisplayName(TextStyle.SHORT, Locale.ENGLISH); // Mon
    
      DayOfWeek.MONDAY.getDisplayName(TextStyle.FULL, Locale.KOREAN); // 월요일
      DayOfWeek.MONDAY.getDisplayName(TextStyle.NARROW, Locale.KOREAN); // 월
      DayOfWeek.MONDAY.getDisplayName(TextStyle.SHORT, Locale.KOREAN); // 월
    
      Month.FEBRUARY.getDisplayName(TextStyle.FULL, Locale.US); // February
      Month.FEBRUARY.getDisplayName(TextStyle.FULL, Locale.KOREA); // 2월
    

참고

[spring security] enum으로 @Secured 권한 관리하기

Spring Security Annotations


spring security는 @Secured나 @PreAuthorize 같은 어노테이션을 사용하여 메소드에 권한을 줄 수 있다. Secured 어노테이션은 @Secured("ROLE_USER") 와 같이 권한 이름을 파라미터로 사용하는데, 이 권한 이름들을 Enum에 정의하고, 사용해보자.

아래와 같은 권한용 Enum이 있다.

import org.springframework.security.core.GrantedAuthority;

public enum MyAuthority implements GrantedAuthority{

    USER("ROLE_USER", "유저권한"),
    ADMIN("ROLE_ADMIN", "어드민권한");

    private String authority;
    private String description;

    private MyAuthority(String authority, String description){
        this.authority = authority;
        this.description = description;
    }

    @Override
    public String getAuthority() {
        return authority;
    }

    public String getDescription() {
        return description;
    }

}

그리고 나서 @Secured에서 아래와 같이 사용해보자.

@Secured(MyAuthority.USER.getAuthority())

@Secured안에는 String이 들어가고, getAuthority()는 String을 리턴하는데, "The value for annotation attribute Secured.value must be a constant expression" 라는 컴파일 에러가 났다. 어노테이션안에 Enum을 사용한 적은 많은 것 같은데, 왜 에러일까?

Enum 타입 사용이 가능했던 어노테이션들은 실제로 Enum을 받도록 만들어진 어노테이션이였다. @RequestMapping의 method를 예로들면, 타입이 String이 아닌 RequestMethod 라는 Enum을 받도록 되어있다. 만약 @Secured에서 Enum을 사용하려 했다면, String이 아닌 Enum을 받도록 설계되어 있어야 한다.

그렇다면 방법은 전혀 없는 것일까? 차선책으로는 static 레벨에 final로 권한을 선언하는 것이다. Enum 안에 ROLE_USER와 같은 권한들은 객체안에 생성되서 컴파일러가 가변적이라고 판단하는 것 같다. 그래서 코드를 아래와 같이 바꾸었다.

import org.springframework.security.core.GrantedAuthority;

public enum MyAuthority implements GrantedAuthority{

    USER(ROLES.USER, "유저권한"),
    ADMIN(ROLES.ADMIN, "어드민권한");

    public static class ROLES{
        public static final String USER = "ROLE_USER";
        public static final String ADMIN = "ROLE_ADMIN";
    }

    private String authority;
    private String description;

    private MyAuthority(String authority, String description){
        this.authority = authority;
        this.description = description;
    }

    @Override
    public String getAuthority() {
        return authority;
    }

    public String getDescription() {
        return description;
    }
}

@Secured를 사용하는 부분은 아래와 같이 바꾸면 잘 된다.

@Secured(MyAuthority.ROLES.USER)

기존의 목표였던 권한을 Enum에서 관리하면서, Secured 어노테이션에서도 그 권한을 참조 할 수 있게 되었다. String을 파라미터로 받는 어노테이션에 변수를 전달하고 싶을 때는 final로 선언되어 있어야 한다.