korean IT student

Keycloak 17.0.1 Spring boot 회원가입, 로그인 구현 - 3 본문

Service/Keycloak

Keycloak 17.0.1 Spring boot 회원가입, 로그인 구현 - 3

현창이 2022. 4. 21. 17:22

앞에서 스프링 시큐리티 연동 방법을 알아보았습니다.

이번 시간에는 스프링 부트에 admin-console을 연동하여 회원가입 및 로그인을 구현해보겠습니다. 

 

앞에서 이어서 작성하겠습니다!

 

목차 

1. spring boot 회원가입, 로그인 구현 및 확인

 

 

 

1. spring boot 회원가입, 로그인 구현 및 확인

    // Keycloak 유저 생성, 조회
    implementation 'org.keycloak:keycloak-admin-client:17.0.1'

admin-console을 조작하기 위하여 추가합니다.

 

@Configuration
public class KeycloakConfiguration {
  @Value("${keycloak.auth-server-url}")
  private String authServerUrl;

  @Value("${keycloak.realm}")
  private String realm;

  @Value("${keycloak.resource}")
  private String clientId;

  @Value("${keycloak.credentials.secret}")
  private String clientSecret;

  /*
  *  keycloak.json 대신에 Spring Boot yml 파일을 이용하도록 돕는다.
  * */
  @Bean
  public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
    return new KeycloakSpringBootConfigResolver();
  }

  /*
   *  Keycloak 서버와 통신하기 위한 클라이언트 빌더
   * */
  @Bean
  public Keycloak keycloak() {
    return KeycloakBuilder.builder()
        .serverUrl(authServerUrl)
        .realm(realm)
        .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
        .clientId(clientId)
        .clientSecret(clientSecret)
        .build();
//      .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build()).build();

//     아이디/ 패스워드로 keycloak 접속 인증
//     KeycloakBuilder.builder()
//        .serverUrl(authServerUrl)
//        .realm(realm)
//        .clientId(clientId)
//        .grantType(OAuth2Constants.PASSWORD)
//        .clientSecret(clientSecret)
//        .username(username)
//        .password(password)
//        .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build()).build();
  }
}

 

- 이전에 환경설정에 등록한 keycloak 값을 세팅합니다. (application.yml 이전 참조)

- 세팅한 값들이 keycloakBuilder를 통해 세팅이 됩니다.

-OAuth2Constants.CLIENT_CREDENTIALS 를 이용하여 인증을 위하여 위와 같이 on이 되어 있어야 합니다.

- 위치는 왼쪽 이미지를 참조 하시면 됩니다. clients > 생성한 client > settings

 

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/users")
public class AuthController {

  private final AuthService authService;

  /*
  * 회원가입
  * */
  @PostMapping("/signup")
  public ResponseEntity registerUser(@RequestBody UserDto userDto) {
    if(authService.existsByUsername(userDto.getEmail())) {
      return ResponseEntity.ok(Result.fail("유저가 존재합니다."));
    }

    UserDto result = authService.createUser(userDto);
    return ResponseEntity.ok(Result.success(result));
  }

  /*
  *  로그인
  * */
  @PostMapping(path = "/signin")
  public ResponseEntity<?> authenticateUser(@RequestBody  UserDto userDto) {

    AccessTokenResponse response = authService.setAuth(userDto);
    return ResponseEntity.ok(Result.success(response));
  }
}

@Data
public class UserDto {

  private String email;
  
  private String password;
  @NotNull
  private UserRole userRole;
  
  private int status;
  private String statusInfo;

}


@RequiredArgsConstructor
@Getter
public enum UserRole {

    ADMIN("관리자"),
    USER("사용자");


    private final String title;

    public static UserRole of(String title) {
        return Arrays.stream(values())
                .filter(v -> v.getTitle().equals(title))
                .findFirst().orElseThrow(() -> new BusinessException(title));
    }

    public String getCode() {
        return name();
    }
}

- 회원가입은 기존 키클락 유저 이름이 있는지 체크 후 없으면 생성하는 로직입니다.

- 로그인은 회원가입된 키 클락 유저가 있으면 로그인하는 로직입니다.

- UserDto, UserRole은 다른 클래스인데 참고용으로 같이 올렸습니다. 

- UserDto : 데이터 전달

- UserRole : 권한 

 

@Slf4j
@RequiredArgsConstructor
@Service
public class AuthService {

  @Value("${keycloak.auth-server-url}")
  private String authServerUrl;

  @Value("${keycloak.realm}")
  private String realm;

  @Value("${keycloak.resource}")
  private String clientId;

  @Value("${keycloak.credentials.secret}")
  private String clientSecret;

  private final Keycloak keycloak;

  public UserDto createUser(UserDto userDto) {

    // 유저정보 세팅
    UserRepresentation user = new UserRepresentation();
    user.setEnabled(true);
    user.setUsername(userDto.getEmail());

    // Get realm
    RealmResource realmResource = keycloak.realm(realm);
    UsersResource usersResource = realmResource.users();

    Response response = usersResource.create(user);
    if(response.getStatus() == 201) {

      String userId = CreatedResponseUtil.getCreatedId(response);

      // create password credential
      CredentialRepresentation passwordCred = new CredentialRepresentation();
      passwordCred.setTemporary(false);
      passwordCred.setType(CredentialRepresentation.PASSWORD);
      passwordCred.setValue(userDto.getPassword());
      log.info("Created userId {}", userId);
      UserResource userResource = usersResource.get(userId);

      // Set password credential
      userResource.resetPassword(passwordCred);

      // role 세팅
      ClientRepresentation clientRep = realmResource.clients().findByClientId(clientId).get(0);
      RoleRepresentation clientRoleRep = realmResource.clients().get(clientRep.getId()).roles().get(userDto.getUserRole().getCode()).toRepresentation();
      userResource.roles().clientLevel(clientRep.getId()).add(Arrays.asList(clientRoleRep));

    }

    userDto.setStatus(response.getStatus());
    userDto.setStatusInfo(response.getStatusInfo().toString());

    return userDto;
  }

  public AccessTokenResponse setAuth(UserDto userDto) {
    Map<String, Object> clientCredentials = new HashMap<>();
    clientCredentials.put("secret", clientSecret);
    clientCredentials.put("grant_type", "password");

    Configuration configuration =
        new Configuration(authServerUrl, realm, clientId, clientCredentials, null);
    AuthzClient authzClient = AuthzClient.create(configuration);

    AccessTokenResponse response =
        authzClient.obtainAccessToken(userDto.getEmail(), userDto.getPassword());

    return response;
  }

 /*
 *  사용자 존재하는지 체크
 * */
  public boolean existsByUsername(String userName) {

    List<UserRepresentation> search = keycloak.realm(realm).users()
        .search(userName);
    if(search.size() > 0){
      log.debug("search : {}", search.get(0).getUsername());
      return true;
    }
    return false;
  }
}

- createUser메서드를 통하여 사용자정보를 입력받고 keyclock에 등록합니다.

- setAuth 메서드에서 액세스 토큰을 인증하고 검색합니다.

- existsByUsername 메서드에서 사용자 존재 유무 체크합니다.

 

세팅 후 포스트맨으로 유저 등록을 실행하면 에러 나는데 유저 생성 권한이 없어서 나는 에러입니다. 그래서 권한을 추가해야 합니다.

 

- 위와 같이 세팅 후 스프링 서버 재시작하면 됩니다.

 

- 포스트맨으로 성공을 확인하였고 admin-console을 보면 유저가 등록되었음을 확인할 수 있습니다.

 

- 같은 사용자로 다시 등록하면 사용자 존재 여부 체크에 의해 에러 메시지가 출력됩니다.

 

- 유저 조회를 하면 성공적으로 조회가 됨을 확인하였습니다.

 

이로써 회원가입 및 로그인 구현을 해보았습니다!!

 

 

[참고]

https://www.tutorialsbuddy.com/keycloak-secure-spring-boot-rest-api

'Service > Keycloak' 카테고리의 다른 글

Keycloak - 토큰값에 Groups 추가법  (0) 2022.05.02
Keycloak 17.0.1 Spring boot 연동 - 2  (0) 2022.04.21
Keycloak 17.0.1 설치 및 DB 세팅 - 1  (0) 2022.04.20
Keycloak 이란  (0) 2022.04.19
Comments