Я создал конечную точку для возврата токена доступа к моему приложению. Когда я конвертирую json из токена в объект с именем user, почтальон продолжает его обрабатывать и сообщает о тайм-ауте. В журнале весенней загрузки ошибок не отображается. Как ни странно, если я преобразую его в строку, токен возвращается нормально.
Моя конечная точка:
@RequestMapping("/public/token")
@RestController
public class TokenController {
@Value("${KEYCLOAK_URL}")
private String KEYCLOAK_URL;
private static final String CLIENT_ID = "my-app";
private static final String GRANTYPE = "password";
@PostMapping
public ResponseEntity<User> token(@RequestBody Login login) {
HttpHeaders headers = new HttpHeaders();
RestTemplate rt = new RestTemplate();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("client_id", CLIENT_ID);
formData.add("username", login.username);
formData.add("password", login.password);
formData.add("grant_type", GRANTYPE);
HttpEntity<MultiValueMap<String, String>> entity
= new HttpEntity<MultiValueMap<String,String>>(formData, headers);
var user = rt.postForEntity( KEYCLOAK_URL + "/protocol/openid-connect/token", entity, User.class);
return user;
}
public record Login(String username, String password) {}
}
Класс пользователя
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class User {
@JsonProperty("access_token")
private String acessToken;
@JsonProperty("expires_in")
private Long expiresIn;
@JsonProperty("refresh_expires_in")
private Long refreshExpiresIn;
@JsonProperty("refresh_token")
private String refreshToken;
@JsonProperty("token_type")
private String tokenType;
}
Почтальон
Происходит преобразование json из токена в пользователя. Я не смог определить причину бесконечной обработки при возврате этого объекта.
2 года назад я написал проект Keycloak для своего использования, который, я думаю, может быть вам полезен.
https://github.com/bizhan-laripour/keycloak-sso
package com.sso.keycloak.service;
import com.sso.keycloak.dto.*;
import com.sso.keycloak.exception.CustomException;
import com.sso.keycloak.mapper.UserMapper;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.ws.rs.core.Response;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class KeycloakService {
@Value("${keycloak.credentials.secret}")
private String SECRETKEY;
@Value("${keycloak.resource}")
private String CLIENTID;
@Value("${keycloak.auth-server-url}")
private String AUTHURL;
@Value("${keycloak.realm}")
private String REALM;
private final Keycloak keycloak;
private UserMapper userMapper;
@Autowired
public KeycloakService(Keycloak keycloak, UserMapper userMapper) {
this.keycloak = keycloak;
this.userMapper = userMapper;
}
public String getToken(UserCredentials userCredentials) {
String responseToken = null;
try {
String username = userCredentials.getUsername();
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
urlParameters.add(new BasicNameValuePair("grant_type", "password"));
urlParameters.add(new BasicNameValuePair("client_id", CLIENTID));
urlParameters.add(new BasicNameValuePair("username", username));
urlParameters.add(new BasicNameValuePair("password", userCredentials.getPassword()));
urlParameters.add(new BasicNameValuePair("client_secret", SECRETKEY));
responseToken = sendPost(urlParameters);
} catch (Exception e) {
e.printStackTrace();
}
return responseToken;
}
private String sendPost(List<NameValuePair> urlParameters) throws Exception {
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(AUTHURL + "/realms/" + REALM + "/protocol/openid-connect/token");
post.setEntity(new UrlEncodedFormEntity(urlParameters));
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
return result.toString();
}
Этот класс содержит методы для получения токена
package com.sso.keycloak.dto;
import java.util.List;
public class SsoResponse {
private String status;
private String statusMessage;
private String accessToken;
private List<Module> modules;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatusMessage() {
return statusMessage;
}
public void setStatusMessage(String statusMessage) {
this.statusMessage = statusMessage;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public List<Module> getModules() {
return modules;
}
public void setModules(List<Module> modules) {
this.modules = modules;
}
}
Этот класс является dto для заполнения ответа
package com.sso.keycloak.dto;
public class UserCredentials {
private String password;
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Этот класс представляет собой dto, содержащий имя пользователя и пароль для авторизации.
package com.sso.keycloak.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sso.keycloak.dto.SsoResponse;
import com.sso.keycloak.dto.UserCredentials;
import com.sso.keycloak.exception.CustomException;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class LoginService {
private KeycloakService keycloakService;
@Autowired
public LoginService(KeycloakService keycloakService){
this.keycloakService = keycloakService;
}
public SsoResponse loginResponse(UserCredentials userCredentials){
SsoResponse ssoResponse = new SsoResponse();
String token = keycloakService.getToken(userCredentials);
ObjectMapper objectMapper = new ObjectMapper();
try {
Map<String, String> map = objectMapper.readValue(token, Map.class);
ssoResponse.setAccessToken(map.get("access_token"));
ssoResponse.setModules(keycloakService.getRolesOfAUser(userCredentials));
ssoResponse.setStatus("SUCCESS");
ssoResponse.setStatusMessage("Request has been processed successfully");
return ssoResponse;
}catch (Exception exception){
throw new CustomException(HttpStatus.SC_BAD_REQUEST , "login failed");
}
}
}
Этот класс представляет собой сервис для получения токена.
package com.sso.keycloak.controller;
import com.sso.keycloak.dto.UserCredentials;
import com.sso.keycloak.service.KeycloakService;
import com.sso.keycloak.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/login")
public class LoginController {
private final LoginService loginService;
@Autowired
public LoginController(LoginService loginService){
this.loginService = loginService;
}
@RequestMapping(value = "login" , method = RequestMethod.POST)
public ResponseEntity<?> login(@RequestBody UserCredentials userCredentials){
return new ResponseEntity<>(loginService.loginResponse(userCredentials), HttpStatus.OK);
}
}
И это контроллер для получения ssoResponse в ResponseEntity.
Хорошо, верно, я прикреплю сюда полный код вашего вопроса.
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - Из отзыва