Конечная точка не отвечает, когда я использую преобразование в объект

Я создал конечную точку для возврата токена доступа к моему приложению. Когда я конвертирую 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 из токена в пользователя. Я не смог определить причину бесконечной обработки при возврате этого объекта.

1
0
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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.

Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - Из отзыва

dan1st 09.03.2024 20:45

Хорошо, верно, я прикреплю сюда полный код вашего вопроса.

Bizhan Laripour 09.03.2024 21:02

Другие вопросы по теме