Cherry pick marker verify

This commit is contained in:
Eduard Tihomirov 2025-01-21 11:30:09 +03:00
parent b61e82dbd6
commit fbff1ec766
5 changed files with 157 additions and 4 deletions

View file

@ -47,6 +47,12 @@ public class EsiaConfig {
@Value("${esia.token.url:aas/oauth2/v3/te}")
private String esiaTokenUrl;
@Value("${sign.verify.url}")
private String signVerifyUrl;
@Value("${esia.issuer.url}")
private String esiaIssuerUrl;
public String getEsiaScopes() {
String[] scopeItems = esiaScopes.split(",");
return String.join(" ", Arrays.stream(scopeItems).map(String::trim).toArray(String[]::new));
@ -93,4 +99,12 @@ public class EsiaConfig {
public String getLogoutRedirectUrl() {
return logoutRedirectUrl;
}
public String getSignVerifyUrl() {
return signVerifyUrl;
}
public String getEsiaIssuerUrl() {
return esiaIssuerUrl;
}
}

View file

@ -0,0 +1,54 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EsiaHeader implements Serializable {
private static final long serialVersionUID = 1L;
private String alg;
private String ver;
private String sbt;
private String typ;
public String getAlg() {
return alg;
}
public void setAlg(String alg) {
this.alg = alg;
}
public String getVer() {
return ver;
}
public void setVer(String ver) {
this.ver = ver;
}
public String getSbt() {
return sbt;
}
public void setSbt(String sbt) {
this.sbt = sbt;
}
public String getTyp() {
return typ;
}
public void setTyp(String typ) {
this.typ = typ;
}
}

View file

@ -31,12 +31,13 @@ import ru.micord.ervu.kafka.model.Person;
import ru.micord.ervu.kafka.model.Response;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.esia.exception.EsiaException;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.model.EsiaHeader;
import ru.micord.ervu.security.esia.model.EsiaTokenResponse;
import ru.micord.ervu.security.esia.model.FormUrlencoded;
import ru.micord.ervu.security.esia.model.PersonModel;
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
import ru.micord.ervu.security.esia.config.EsiaConfig;
import ru.micord.ervu.security.esia.model.FormUrlencoded;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.model.EsiaTokenResponse;
import ru.micord.ervu.security.esia.model.PersonModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -211,6 +212,10 @@ public class EsiaAuthService {
throw new IllegalStateException("Esia response error. " + errMsg);
}
esiaAccessTokenStr = tokenResponse.getAccess_token();
String verifyResult = verifyToken(esiaAccessTokenStr);
if (verifyResult != null) {
throw new EsiaException(verifyResult);
}
String esiaRefreshTokenStr = tokenResponse.getRefresh_token();
EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr);
prnOid = esiaAccessToken.getSbj_id();
@ -287,6 +292,10 @@ public class EsiaAuthService {
throw new IllegalStateException("Esia response error. " + errMsg);
}
String esiaAccessTokenStr = tokenResponse.getAccess_token();
String verifyResult = verifyToken(esiaAccessTokenStr);
if (verifyResult != null) {
throw new EsiaException(verifyResult);
}
String esiaNewRefreshTokenStr = tokenResponse.getRefresh_token();
EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr);
String prnOid = esiaAccessToken.getSbj_id();
@ -400,4 +409,57 @@ public class EsiaAuthService {
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
private String verifyToken(String accessToken) {
EsiaAccessToken esiaAccessToken = personalDataService.readToken(accessToken);
EsiaHeader esiaHeader = personalDataService.readHeader(accessToken);
if (!esiaHeader.getSbt().equals("access")) {
return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid";
}
if (!esiaHeader.getTyp().equals("JWT")) {
return "Token invalid. Token type: " + esiaHeader.getTyp() + " invalid";
}
if (!esiaAccessToken.getClient_id().equals(esiaConfig.getClientId())) {
return "Token invalid. Token clientId: " + esiaAccessToken.getClient_id() + " invalid";
}
if (!esiaAccessToken.getIss().equals(esiaConfig.getEsiaIssuerUrl())) {
return "Token invalid. Token issuer:" + esiaAccessToken.getIss() + " invalid";
}
//TODO SUPPORT-8750
// LocalDateTime iatTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getIat()),
// ZoneId.systemDefault()
// );
// LocalDateTime expTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getExp()),
// ZoneId.systemDefault()
// );
// LocalDateTime currentTime = LocalDateTime.now();
// if (!currentTime.isAfter(iatTime) || !expTime.isAfter(iatTime)) {
// return "Token invalid. Token expired";
// }
HttpResponse<String> response = signVerify(accessToken);
if (response.statusCode() != 200) {
if (response.statusCode() == 401) {
return "Token invalid. " + response.body();
}
return "Error in verify module. Error status " + response.statusCode();
}
return null;
}
private HttpResponse<String> signVerify(String accessToken) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(esiaConfig.getSignVerifyUrl()))
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofString(accessToken, StandardCharsets.UTF_8))
.build();
return HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(request, HttpResponse.BodyHandlers.ofString());
}
catch (Exception e) {
throw new EsiaException(e);
}
}
}

View file

@ -8,8 +8,10 @@ import java.time.Duration;
import java.util.Base64;
import com.fasterxml.jackson.databind.ObjectMapper;
import ru.micord.ervu.security.esia.exception.EsiaException;
import ru.micord.ervu.security.esia.config.EsiaConfig;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.model.EsiaHeader;
import ru.micord.ervu.security.esia.model.PassportModel;
import ru.micord.ervu.security.esia.model.PersonModel;
import org.springframework.beans.factory.annotation.Autowired;
@ -103,4 +105,23 @@ public class EsiaPersonalDataService implements PersonalDataService {
throw new RuntimeException(e);
}
}
@Override
public EsiaHeader readHeader(String accessToken) {
try {
byte[] decodedBytes = Base64.getDecoder()
.decode(
accessToken.substring(0, accessToken.indexOf('.'))
.replace('-', '+')
.replace('_', '/'));
String decodedString = new String(decodedBytes);
EsiaHeader esiaHeader = objectMapper.readValue(decodedString,
EsiaHeader.class
);
return esiaHeader;
}
catch (Exception e) {
throw new EsiaException(e);
}
}
}

View file

@ -1,6 +1,7 @@
package ru.micord.ervu.security.esia.service;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.model.EsiaHeader;
import ru.micord.ervu.security.esia.model.PersonModel;
/**
@ -10,4 +11,5 @@ public interface PersonalDataService {
PersonModel getPersonModel(String accessToken);
EsiaAccessToken readToken(String accessToken);
EsiaHeader readHeader(String accessToken);
}