Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

두리공장

[keycloak] User Storage SPI 사용시 Create at 필드 동기화 본문

cloud

[keycloak] User Storage SPI 사용시 Create at 필드 동기화

두리공장 2023. 6. 6. 23:51

지난번에 singular-keycloak-database-federation 를 사용하여 keycloak 에서 external Database 를 연결하는 것을 시도해 보았다.
동기화는 잘 되었는데 user 에 보면 create at 부분이 "invalid Date" 라고 뜨는 문제가 있다.

여기에 날짜를 정상적으로 보여주기로 했다.
UserAdapter 는 AbstractUserAdapterFederatedStorage 를 상속하여 사용하는데, 
이걸 열어보면,

    public static String FIRST_NAME_ATTRIBUTE = "FIRST_NAME";
    public static String LAST_NAME_ATTRIBUTE = "LAST_NAME";
    public static String EMAIL_ATTRIBUTE = "EMAIL";
    public static String EMAIL_VERIFIED_ATTRIBUTE = "EMAIL_VERIFIED";
    public static String CREATED_TIMESTAMP_ATTRIBUTE = "CREATED_TIMESTAMP";
    public static String ENABLED_ATTRIBUTE = "ENABLED";

이렇게 되어 있다. 속성값으로 createdTimestamp 를 사용하는 것이다. get / set 메서드가 지원되기에 이걸 사용하면 외부DB에 있는 값은 바인딩할 수 있겠다.
먼저 UserAdapter 에 createTime 을 셋팅하도록 코드를 수정했다.

    public UserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, Map<String, String> data, boolean allowDatabaseToOverwriteKeycloak, List<Map<String, String>> userRoles) {
        super(session, realm, model);
        this.keycloakId = StorageId.keycloakId(model, data.get("id"));
        this.username = data.get("username");
        this.userRoles = userRoles;
        try {
          Map<String, List<String>> attributes = this.getAttributes();
          for (Entry<String, String> e : data.entrySet()) {
              Set<String>  newValues = new HashSet<>();
              if (!allowDatabaseToOverwriteKeycloak) {
                List<String> attribute = attributes.get(e.getKey());
                if (attribute != null) {
                    newValues.addAll(attribute);
                }
              }
              newValues.add(StringUtils.trimToNull(e.getValue()));
              this.setAttribute(e.getKey(), newValues.stream().filter(Objects::nonNull).collect(Collectors.toList()));
          }

          // create Time 을 가져온다.
          log.info("createdTimestamp:" + data.get("createdTimestamp"));
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
          if(!data.get("createdTimestamp").isEmpty() || data.get("createdTimestamp") != null) {
              Date dt = sdf.parse(data.get("createdTimestamp"));
              this.setCreatedTimestamp(dt.getTime());
          }


        } catch(Exception e) {
          log.errorv(e, "UserAdapter constructor, username={0}", this.username);
        }
    }

이렇게 하면, Map<String, String> data 로 받는 값에서 createdTimestamp 값을 꺼내어 this.setCreatedTimestamp(dt.getTime()); 으로 값을 넣을 수 있다.

DB 에 cratedTimestamp 필드를 추가하고 timestamp 형식으로 값을 추가하자

alter table users add column 
"createdTimestamp" timestamp;

update users set
"createdTimestamp" = now()
where id='durifac';

그리고 SPI 의 쿼리문에도 createdTimestamp 필드를 추가해준다. (참고로 postgresql 은 대소문자를 구분하므로, 필드명은 쌍따옴표로 묶어준다.

select id,       "createdTimestamp",     username,            email,            firstName,            lastName,            cpf,            fullName from public.users


도커를 재기동하자(restart)
이제 정상적으로 표시된다.