728x90
1. auth 서비스 - 회원가입
AuthController
authService를 통해 keycloack DB에 사용자 생성 후, userService를 통해 부가적인 사용자 데이터를 user DB에 저장
// 회원가입
@PostMapping("/signup")
public ResponseEntity<String> registerUser(UserDTO userDto) {
if(authService.existsByUsername(userDto.getUserId())) {
return new ResponseEntity<>("유저가 존재합니다.", HttpStatus.CONFLICT);
}
try {
UserDTO userDTO = authService.createUser(userDto); // keycloak 사용자 생성
userService.registerUser(userDTO);
} catch (Exception e) {
log.error(e.getMessage());
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("회원가입에 성공했습니다.", HttpStatus.CREATED);
}
UserDto
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
private String userId;
private String name;
private String job;
private int age;
private String gender;
private String phone;
private String mailAddr;
private UserRole userRole;
private MultipartFile userImage;
private String imageUrl;
private String userPwd;
private int status;
private String statusInfo;
}
AuthService
application.yml의 값을 사용
@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;
keycloak API로 사용자 생성
public UserDTO createUser(UserDTO userDto) {
// 유저정보 세팅
UserRepresentation user = new UserRepresentation();
user.setEnabled(true);
user.setUsername(userDto.getUserId());
// 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.getUserPwd());
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;
}
2. auth 서비스 - 로그인(토큰 발급)
AuthController
아이디와 비밀번호를 파라미터로 받고, 키클록 토큰 발급에 성공하면 아이디에 따른 사용자 정보를 함께 반환
// 로그인
@PostMapping(path = "/signin")
public ResponseEntity authenticateUser(@RequestBody HashMap<String, String> map) {
AccessTokenResponse tokenRes = authService.setAuth(map); // Token 발급
Map<String, Object> data = new HashMap<String, Object>();
data.put("token", tokenRes);
if(tokenRes != null){
data.put("info", userService.getUserDetailInfo(map.get("username")));
}
Map<String, Object> resultmap = new HashMap<String, Object>();
resultmap.put("data", data);
return ResponseEntity.ok(resultmap);
}
AuthService
keycloak API로 토큰 발급
public AccessTokenResponse setAuth(HashMap<String, String> map) {
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(map.get("username"), map.get("password"));
// authzClient.obtainAccessToken(아이디, 비밀번호);
return response;
}
3. auth 서비스 - 토큰 리프레시
AuthController
리프레시 토큰을 파라미터로 받고, 키클록 토큰 발급에 성공하면 아이디에 따른 사용자 정보를 함께 반환
// refresh token
@PostMapping(path = "/refresh_token")
public ResponseEntity refreshToken(@RequestBody HashMap<String, String> map) {
String refreshToken = map.get("refresh_token");
Map<String, Object> tokenRes = authService.refreshToken(refreshToken);
Map<String, Object> data = new HashMap<String, Object>();
data.put("token", tokenRes.get("token"));
if(tokenRes != null){
data.put("info", userService.getUserDetailInfo((String) tokenRes.get("username")));
}
return ResponseEntity.ok(data);
}
AuthService
grant_type을 refresh_token으로 토큰 발급, 발급에 성공하면 토큰과 사용자 아이디를 반환
public Map<String, Object> refreshToken(String refreshToken) {
String url = authServerUrl + "realms/" + realm + "/protocol/openid-connect/token";
String url2 = authServerUrl + "realms/" + realm + "/protocol/openid-connect/userinfo";
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("refresh_token", refreshToken);
map.add("grant_type", "refresh_token");
map.add("client_id", clientId);
map.add("client_secret", clientSecret);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<?> entity = new HttpEntity<>(map, headers);
Map<String, Object> data = new HashMap<String, Object>();
ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, entity, Map.class);
data.put("token", response.getBody());
if (response.getStatusCodeValue() == 200 ) {
headers.add( "Authorization","Bearer " + response.getBody().get("access_token"));
ResponseEntity<Map> response2 = restTemplate.exchange( url2, HttpMethod.POST, entity, Map.class );
data.put("username", response2.getBody().get("preferred_username"));
}
return data;
}
더보기
@Slf4j
@Service
@RequiredArgsConstructor
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.getUserId());
// 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.getUserPwd());
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());
return userDto;
}
public AccessTokenResponse setAuth(HashMap<String, String> map) {
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(map.get("username"), map.get("password"));
return response;
}
public Map<String, Object> refreshToken(String refreshToken) {
String url = authServerUrl + "realms/" + realm + "/protocol/openid-connect/token";
String url2 = authServerUrl + "realms/" + realm + "/protocol/openid-connect/userinfo";
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("refresh_token", refreshToken);
map.add("grant_type", "refresh_token");
map.add("client_id", clientId);
map.add("client_secret", clientSecret);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<?> entity = new HttpEntity<>(map, headers);
Map<String, Object> data = new HashMap<String, Object>();
ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, entity, Map.class);
data.put("token", response.getBody());
if (response.getStatusCodeValue() == 200 ) {
headers.add( "Authorization","Bearer " + response.getBody().get("access_token"));
ResponseEntity<Map> response2 = restTemplate.exchange( url2, HttpMethod.POST, entity, Map.class );
data.put("username", response2.getBody().get("preferred_username"));
}
return data;
}
// 사용자 존재하는지 체크
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;
}
}
4. 토큰 체크
controller단에서 Principal 객체를 사용해 JwtAuthenticationToken 사용자 권한 및 아이디 조회
@PostMapping("/list")
public ResponseEntity<PageResultDTO<GroupDTO, Group>> getGroupList(Principal principal, PageRequestDTO pageRequestDTO) {
JwtAuthenticationToken token = (JwtAuthenticationToken) principal;
//사용자 아이디 조회
String userId = token.getTokenAttributes().get("preferred_username").toString();
return new ResponseEntity<>( groupService.getGroupList(userId, pageRequestDTO), HttpStatus.OK);
}
@GetMapping("/menu")
public List<MenuDTO> getUserMenuDTO(Principal principal) {
if (principal != null) { // 토큰이 있는 경우
JwtAuthenticationToken token = (JwtAuthenticationToken) principal;
Map<String,Object> resource_access = (Map<String,Object>) token.getTokenAttributes().get("resource_access");
Map<String, Object> team_cloud_client = (Map<String, Object>) resource_access.get("team_cloud_client");
// 사용자 권한 조회
ArrayList<String> roles = (ArrayList) team_cloud_client.get("roles");
if (roles.get(0).equals("USER")) { // USER 권한인 경우
return commonService.getUserMenuDTOList();
} else if (roles.get(0).equals("ADMIN")) { // ADMIN 권한인 경우
return commonService.getAdminMenuDTOList();
}
}
return commonService.getAllMenuDTOList();
}
각 서비스에 생성한 security config 파일로 @PreAuthorize를 키클록의 권한으로 설정
@PreAuthorize("hasRole('ROLE_USER')") : USER 권한을 가진 사용자만 접근
// 설문 생성 리스트 조회
@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping(value = "/make_list", method = RequestMethod.GET)
public ResponseEntity<Page<SurveyDTO>> getMakeList(
@RequestParam (value = "category", required = false) Integer[] categoryId,
@RequestParam (value = "status", required = false) SurveyStatus status,
@RequestParam (value = "title", required = false) String title,
Principal principal, PageRequestDTO pageRequestDTO) {
JwtAuthenticationToken token = (JwtAuthenticationToken) principal;
String userId = token.getTokenAttributes().get("preferred_username").toString();
Page<SurveyDTO> list = surveyService.getSurveyMakeList(title, userId, categoryId, status, pageRequestDTO);
return new ResponseEntity<>(list, HttpStatus.OK);
}
728x90