Рефакторинг кода
This commit is contained in:
parent
5c89e14e7d
commit
20c67a04fc
|
|
@ -0,0 +1,51 @@
|
|||
package ru.oa2.lti.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(AppProperties.class)
|
||||
public class AppConfig {
|
||||
|
||||
@Autowired
|
||||
AppProperties appProperties;
|
||||
|
||||
@Bean
|
||||
public JwtDecoder jwtDecoder() {
|
||||
return NimbusJwtDecoder
|
||||
.withJwkSetUri(appProperties.lmsUri() + "/lti/keys")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObjectMapper getMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(new JavaTimeModule());
|
||||
mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestClient getRestClient(RestClient.Builder restClientBuilder) {
|
||||
return restClientBuilder
|
||||
.baseUrl(appProperties.lmsUri())
|
||||
.defaultHeader(HttpHeaders.USER_AGENT, "LtiProvider")
|
||||
.defaultStatusHandler(HttpStatusCode::isError, (req, res) -> {
|
||||
throw new RuntimeException("API error: " + res.getStatusCode() + " body: " +
|
||||
new String(res.getBody().readAllBytes(), StandardCharsets.UTF_8)); //TODO business exception
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.oa2.lti.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "lti")
|
||||
public record AppProperties(
|
||||
String lmsUri
|
||||
) {
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package ru.oa2.lti.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class JwtConfig {
|
||||
|
||||
@Bean
|
||||
public JwtDecoder jwtDecoder() {
|
||||
return NimbusJwtDecoder
|
||||
.withJwkSetUri("http://openolat.local/lti/keys") //TODO адрес из application
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package ru.oa2.lti.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MapperConfig {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper getMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.registerModule(new JavaTimeModule());
|
||||
mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package ru.oa2.lti.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Configuration
|
||||
public class RestClientConfig {
|
||||
|
||||
@Bean
|
||||
public RestClient getRestClient(RestClient.Builder restClientBuilder) {
|
||||
return restClientBuilder
|
||||
.baseUrl("http://openolat.local")
|
||||
.defaultHeader(HttpHeaders.USER_AGENT, "SpringBootApp")
|
||||
.defaultStatusHandler(HttpStatusCode::isError, (req, res) -> {
|
||||
throw new RuntimeException("API error: " + res.getStatusCode() + " body: " +
|
||||
new String(res.getBody().readAllBytes(), StandardCharsets.UTF_8)); //TODO business exception
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -48,10 +48,9 @@ public class ResultController {
|
|||
if (session == null) return ResponseEntity.status(401).build();
|
||||
|
||||
var ltiLogin = (LtiLogin) session.getAttribute("lti_login");
|
||||
var assessToken = tokenService.exchangeForAccessToken(ltiLogin.getClientId(), "http://openolat.local/lti/token");
|
||||
var assessToken = tokenService.exchangeForAccessToken(
|
||||
ltiLogin.getClientId());
|
||||
|
||||
String userId = (String) session.getAttribute("lti_user_id");
|
||||
UUID ltiContextId = (UUID) session.getAttribute("lti_context_id");
|
||||
String idToken = (String) session.getAttribute("id_token");
|
||||
|
||||
resultService.setResult(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Scope {
|
||||
public static String LINE_ITEM = "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem";
|
||||
public static String SCOPE = "https://purl.imsglobal.org/spec/lti-ags/scope/scope";
|
||||
public static String ENDPOINT = "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint";
|
||||
|
||||
public static String scope(String... scopes) {
|
||||
List<String> listScopes = new ArrayList<>(Arrays.asList(scopes));
|
||||
return String.join(" ", listScopes);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import org.springframework.util.LinkedMultiValueMap;
|
|||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import ru.oa2.lti.config.AppProperties;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -16,21 +17,24 @@ public class TokenService {
|
|||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
private final RestClient restClient;
|
||||
private final AppProperties appProperties;
|
||||
|
||||
public TokenService(RestClient restClient) {
|
||||
public TokenService(RestClient restClient, AppProperties appProperties) {
|
||||
this.restClient = restClient;
|
||||
this.appProperties = appProperties;
|
||||
}
|
||||
|
||||
// Вызывается после LTI-запуска
|
||||
public String exchangeForAccessToken(
|
||||
String clientId,
|
||||
String tokenEndpoint) {
|
||||
String clientId) {
|
||||
|
||||
var endpointUrl = appProperties.lmsUri() + "/lti/token";
|
||||
|
||||
try {
|
||||
// Генерируем client_assertion
|
||||
String clientAssertion = JwtAssertionGenerator.generateClientAssertion(
|
||||
clientId,
|
||||
tokenEndpoint,
|
||||
endpointUrl,
|
||||
"my-key-id-1" // должен совпадать с тем, что в JWKS
|
||||
);
|
||||
|
||||
|
|
@ -39,7 +43,7 @@ public class TokenService {
|
|||
body.add("grant_type", "client_credentials");
|
||||
body.add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
|
||||
body.add("client_assertion", clientAssertion);
|
||||
body.add("scope", "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem https://purl.imsglobal.org/spec/lti-ags/scope/scope https://purl.imsglobal.org/spec/lti-ags/claim/endpoint");
|
||||
body.add("scope", Scope.scope(Scope.LINE_ITEM, Scope.SCOPE, Scope.ENDPOINT));
|
||||
|
||||
// Заголовки
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
|
@ -48,7 +52,7 @@ public class TokenService {
|
|||
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
|
||||
|
||||
// Отправляем запрос к OpenOLAT
|
||||
ResponseEntity<Map> response = restTemplate.postForEntity(tokenEndpoint, request, Map.class);
|
||||
ResponseEntity<Map> response = restTemplate.postForEntity(endpointUrl, request, Map.class);
|
||||
|
||||
if (response.getStatusCode() == HttpStatus.OK) {
|
||||
Map<String, Object> responseBody = response.getBody();
|
||||
|
|
@ -70,7 +74,7 @@ public class TokenService {
|
|||
"&code=code1" +
|
||||
"&iss=" + iss +
|
||||
"&login_hint=" + loginHint +
|
||||
"&redirect_uri=http://openolat.local/tool/lti/redirect" +
|
||||
"&redirect_uri=" + appProperties.lmsUri() + "/tool/lti/redirect" +
|
||||
"<i_message_hint=" + ltiLoginHint)
|
||||
.retrieve();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,3 +29,6 @@ logging:
|
|||
org.hibernate.SQL: DEBUG
|
||||
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
|
||||
|
||||
|
||||
lti:
|
||||
lms_url: http://openolat.local
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package ru.oa2.lti;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.oa2.lti.service.jwt.Scope;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
public class ScopeTest {
|
||||
|
||||
@Test
|
||||
public void scopeTest() {
|
||||
assertThat(Scope.scope(Scope.ENDPOINT, Scope.LINE_ITEM))
|
||||
.isEqualTo("https://purl.imsglobal.org/spec/lti-ags/claim/endpoint https://purl.imsglobal.org/spec/lti-ags/scope/lineitem");
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,11 @@ package ru.oa2.lti.jwt;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import ru.oa2.lti.config.JwtConfig;
|
||||
import ru.oa2.lti.config.AppConfig;
|
||||
import ru.oa2.lti.service.jwt.JwtService;
|
||||
import ru.oa2.lti.service.jwt.JwtServiceImpl;
|
||||
|
||||
@SpringBootTest(classes = {JwtConfig.class, JwtServiceImpl.class})
|
||||
@SpringBootTest(classes = {AppConfig.class, JwtServiceImpl.class})
|
||||
public class IdTokenTest {
|
||||
|
||||
@Autowired
|
||||
|
|
|
|||
Loading…
Reference in New Issue