Merge branch 'feature/SUPPORT-8942_fix' into develop
This commit is contained in:
commit
93ab3255cb
14 changed files with 196 additions and 106 deletions
|
|
@ -35,7 +35,7 @@ import ru.micord.ervu.exception.JsonParsingException;
|
||||||
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
||||||
import ru.micord.ervu.security.esia.model.PersonModel;
|
import ru.micord.ervu.security.esia.model.PersonModel;
|
||||||
import ru.micord.ervu.security.esia.service.UlDataService;
|
import ru.micord.ervu.security.esia.service.UlDataService;
|
||||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
|
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
|
||||||
import ru.micord.ervu.service.InteractionService;
|
import ru.micord.ervu.service.InteractionService;
|
||||||
|
|
@ -91,7 +91,7 @@ public class EmployeeInfoFileUploadService {
|
||||||
formType);
|
formType);
|
||||||
String esiaUserId = userIdsPair.getEsiaUserId();
|
String esiaUserId = userIdsPair.getEsiaUserId();
|
||||||
String ervuId = userIdsPair.getErvuId();
|
String ervuId = userIdsPair.getErvuId();
|
||||||
String accessToken = EsiaTokensStore.getAccessToken(esiaUserId);
|
String accessToken = EsiaAuthInfoStore.getAccessToken(esiaUserId);
|
||||||
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
|
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
|
||||||
PersonModel personModel = employeeModel.getPerson();
|
PersonModel personModel = employeeModel.getPerson();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ru.micord.ervu.security.esia.token;
|
package ru.micord.ervu.security.esia;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -6,15 +6,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
import ru.micord.ervu.security.esia.model.ExpiringState;
|
||||||
|
import ru.micord.ervu.security.esia.model.ExpiringToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eduard Tihomirov
|
* @author Eduard Tihomirov
|
||||||
*/
|
*/
|
||||||
public class EsiaTokensStore {
|
public class EsiaAuthInfoStore {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
private static final Map<String, ExpiringToken> ACCESS_TOKENS_MAP = new ConcurrentHashMap<>();
|
private static final Map<String, ExpiringToken> ACCESS_TOKENS_MAP = new ConcurrentHashMap<>();
|
||||||
private static final Map<String, ExpiringToken> REFRESH_TOKENS_MAP = new ConcurrentHashMap<>();
|
private static final Map<String, ExpiringToken> REFRESH_TOKENS_MAP = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<String, ExpiringState> PRNS_UUID_STATE_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static void addAccessToken(String prnOid, String token, long expiresIn) {
|
public static void addAccessToken(String prnOid, String token, long expiresIn) {
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
|
|
@ -77,4 +79,25 @@ public class EsiaTokensStore {
|
||||||
REFRESH_TOKENS_MAP.remove(prnOid);
|
REFRESH_TOKENS_MAP.remove(prnOid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addState(String prnsUUID, String state, long expiresIn) {
|
||||||
|
long expiryTime = System.currentTimeMillis() + expiresIn * 1000L;
|
||||||
|
PRNS_UUID_STATE_MAP.put(prnsUUID, new ExpiringState(state, expiryTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getState(String prnsUUID) {
|
||||||
|
return PRNS_UUID_STATE_MAP.get(prnsUUID).getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeState(String prnsUUID) {
|
||||||
|
PRNS_UUID_STATE_MAP.remove(prnsUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeExpiredState() {
|
||||||
|
for (String key : PRNS_UUID_STATE_MAP.keySet()) {
|
||||||
|
ExpiringState state = PRNS_UUID_STATE_MAP.get(key);
|
||||||
|
if (state != null && state.isExpired()) {
|
||||||
|
PRNS_UUID_STATE_MAP.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +62,12 @@ public class EsiaConfig {
|
||||||
@Value("${esia.issuer.url}")
|
@Value("${esia.issuer.url}")
|
||||||
private String esiaIssuerUrl;
|
private String esiaIssuerUrl;
|
||||||
|
|
||||||
|
@Value("${esia.marker.ver}")
|
||||||
|
private String esiaMarkerVer;
|
||||||
|
|
||||||
|
@Value("${esia.state.cookie.life.time:300}")
|
||||||
|
private long esiaStateCookieLifeTime;
|
||||||
|
|
||||||
public String getEsiaOrgScopes() {
|
public String getEsiaOrgScopes() {
|
||||||
String[] scopeItems = esiaOrgScopes.split(",");
|
String[] scopeItems = esiaOrgScopes.split(",");
|
||||||
return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new));
|
return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new));
|
||||||
|
|
@ -125,4 +131,12 @@ public class EsiaConfig {
|
||||||
public String getEsiaIssuerUrl() {
|
public String getEsiaIssuerUrl() {
|
||||||
return esiaIssuerUrl;
|
return esiaIssuerUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEsiaMarkerVer() {
|
||||||
|
return esiaMarkerVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEsiaStateCookieLifeTime() {
|
||||||
|
return esiaStateCookieLifeTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,15 @@ public class EsiaController {
|
||||||
private EsiaDataService esiaDataService;
|
private EsiaDataService esiaDataService;
|
||||||
|
|
||||||
@GetMapping(value = "/esia/url")
|
@GetMapping(value = "/esia/url")
|
||||||
public String getEsiaUrl() {
|
public String getEsiaUrl(HttpServletResponse response) {
|
||||||
return esiaAuthService.generateAuthCodeUrl();
|
return esiaAuthService.generateAuthCodeUrl(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/esia/auth")
|
@GetMapping(value = "/esia/auth")
|
||||||
public void esiaAuth(@RequestParam(value = "code", required = false) String code,
|
public void esiaAuth(@RequestParam String code,
|
||||||
@RequestParam(value = "error", required = false) String error, HttpServletRequest request,
|
@RequestParam String state,
|
||||||
HttpServletResponse response) {
|
HttpServletResponse response, HttpServletRequest request) {
|
||||||
esiaAuthService.authEsiaTokensByCode(code, error, request, response);
|
esiaAuthService.authEsiaTokensByCode(code, state, response, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/esia/refresh")
|
@PostMapping(value = "/esia/refresh")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package ru.micord.ervu.security.esia.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eduard Tihomirov
|
||||||
|
*/
|
||||||
|
public class ExpiringState {
|
||||||
|
private String state;
|
||||||
|
private long expiryTime;
|
||||||
|
|
||||||
|
public ExpiringState(String state, long expiryTime) {
|
||||||
|
this.state = state;
|
||||||
|
this.expiryTime = expiryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExpiryTime() {
|
||||||
|
return expiryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiryTime(long expiryTime) {
|
||||||
|
this.expiryTime = expiryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExpired() {
|
||||||
|
return System.currentTimeMillis() > expiryTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ru.micord.ervu.security.esia.token;
|
package ru.micord.ervu.security.esia.model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eduard Tihomirov
|
* @author Eduard Tihomirov
|
||||||
|
|
@ -28,7 +28,7 @@ public class ExpiringToken {
|
||||||
this.expiryTime = expiryTime;
|
this.expiryTime = expiryTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return System.currentTimeMillis() > expiryTime;
|
return System.currentTimeMillis() > expiryTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package ru.micord.ervu.security.esia.service;
|
||||||
|
|
||||||
|
import net.javacrumbs.shedlock.core.SchedulerLock;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eduard Tihomirov
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class EsiaAuthInfoClearShedulerService {
|
||||||
|
@Scheduled(cron = "${esia.auth.info.clear.cron:0 0 */1 * * *}")
|
||||||
|
@SchedulerLock(name = "clearAuthInfo")
|
||||||
|
@Transactional
|
||||||
|
public void run() {
|
||||||
|
EsiaAuthInfoStore.removeExpiredRefreshToken();
|
||||||
|
EsiaAuthInfoStore.removeExpiredAccessToken();
|
||||||
|
EsiaAuthInfoStore.removeExpiredState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,13 +10,16 @@ import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
|
@ -24,9 +27,11 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import ervu.service.okopf.OkopfService;
|
import ervu.service.okopf.OkopfService;
|
||||||
import org.springframework.context.support.MessageSourceAccessor;
|
import org.springframework.context.support.MessageSourceAccessor;
|
||||||
|
import org.springframework.http.ResponseCookie;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import ru.micord.ervu.audit.constants.AuditConstants;
|
import ru.micord.ervu.audit.constants.AuditConstants;
|
||||||
import ru.micord.ervu.audit.service.AuditService;
|
import ru.micord.ervu.audit.service.AuditService;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
import ru.micord.ervu.security.esia.exception.EsiaException;
|
import ru.micord.ervu.security.esia.exception.EsiaException;
|
||||||
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
||||||
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
||||||
|
|
@ -35,7 +40,7 @@ import ru.micord.ervu.security.esia.model.EsiaTokenResponse;
|
||||||
import ru.micord.ervu.security.esia.model.FormUrlencoded;
|
import ru.micord.ervu.security.esia.model.FormUrlencoded;
|
||||||
import ru.micord.ervu.security.esia.model.OrganizationModel;
|
import ru.micord.ervu.security.esia.model.OrganizationModel;
|
||||||
import ru.micord.ervu.security.esia.model.SignResponse;
|
import ru.micord.ervu.security.esia.model.SignResponse;
|
||||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
|
@ -59,8 +64,6 @@ import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||||
import ru.cg.webbpm.modules.core.runtime.api.LocalizedException;
|
import ru.cg.webbpm.modules.core.runtime.api.LocalizedException;
|
||||||
import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils;
|
import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils;
|
||||||
|
|
||||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUsername;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eduard Tihomirov
|
* @author Eduard Tihomirov
|
||||||
*/
|
*/
|
||||||
|
|
@ -69,6 +72,7 @@ public class EsiaAuthService {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor(
|
private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor(
|
||||||
"messages/common_errors_messages");
|
"messages/common_errors_messages");
|
||||||
|
private static final String PRNS_UUID = "prns_uuid";
|
||||||
@Autowired
|
@Autowired
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
@ -91,13 +95,14 @@ public class EsiaAuthService {
|
||||||
@Value("${ervu.kafka.org.request.topic}")
|
@Value("${ervu.kafka.org.request.topic}")
|
||||||
private String requestTopic;
|
private String requestTopic;
|
||||||
|
|
||||||
public String generateAuthCodeUrl() {
|
public String generateAuthCodeUrl(HttpServletResponse response) {
|
||||||
try {
|
try {
|
||||||
String clientId = esiaConfig.getClientId();
|
String clientId = esiaConfig.getClientId();
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
||||||
ZonedDateTime dt = ZonedDateTime.now();
|
ZonedDateTime dt = ZonedDateTime.now();
|
||||||
String timestamp = dt.format(formatter);
|
String timestamp = dt.format(formatter);
|
||||||
String state = UUID.randomUUID().toString();
|
String state = UUID.randomUUID().toString();
|
||||||
|
String prnsUUID = UUID.randomUUID().toString();
|
||||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||||
String redirectUrlEncoded = redirectUrl.replaceAll(":", "%3A")
|
String redirectUrlEncoded = redirectUrl.replaceAll(":", "%3A")
|
||||||
.replaceAll("/", "%2F");
|
.replaceAll("/", "%2F");
|
||||||
|
|
@ -113,7 +118,11 @@ public class EsiaAuthService {
|
||||||
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
||||||
|
|
||||||
String clientSecret = signMap(parameters);
|
String clientSecret = signMap(parameters);
|
||||||
|
EsiaAuthInfoStore.addState(prnsUUID, state, esiaConfig.getEsiaStateCookieLifeTime());
|
||||||
|
ResponseCookie prnsCookie = securityHelper.createCookie(PRNS_UUID, prnsUUID, "/")
|
||||||
|
.maxAge(esiaConfig.getEsiaStateCookieLifeTime())
|
||||||
|
.build();
|
||||||
|
securityHelper.addResponseCookie(response, prnsCookie);
|
||||||
String responseType = "code";
|
String responseType = "code";
|
||||||
|
|
||||||
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaCodeUrl();
|
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaCodeUrl();
|
||||||
|
|
@ -164,22 +173,22 @@ public class EsiaAuthService {
|
||||||
return uriBuilder.toString();
|
return uriBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void authEsiaTokensByCode(String esiaAuthCode, String error,
|
public void authEsiaTokensByCode(String esiaAuthCode, String state, HttpServletResponse response, HttpServletRequest request) {
|
||||||
HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
if (error != null && !error.equals("null")) {
|
|
||||||
throw new EsiaException(error);
|
|
||||||
}
|
|
||||||
String esiaAccessTokenStr = null;
|
String esiaAccessTokenStr = null;
|
||||||
String prnOid = null;
|
String prnOid = null;
|
||||||
Long expiresIn = null;
|
Long expiresIn = null;
|
||||||
boolean hasRole = false;
|
boolean hasRole = false;
|
||||||
long timeSignSecret = 0, timeRequestAccessToken = 0, timeVerifySecret = 0;
|
long timeSignSecret = 0, timeRequestAccessToken = 0, timeVerifySecret = 0;
|
||||||
|
String verifyStateResult = verifyStateFromCookie(request, state, response);
|
||||||
|
if (verifyStateResult != null) {
|
||||||
|
throw new EsiaException(verifyStateResult);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
String clientId = esiaConfig.getClientId();
|
String clientId = esiaConfig.getClientId();
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
||||||
ZonedDateTime dt = ZonedDateTime.now();
|
ZonedDateTime dt = ZonedDateTime.now();
|
||||||
String timestamp = dt.format(formatter);
|
String timestamp = dt.format(formatter);
|
||||||
String state = UUID.randomUUID().toString();
|
String newState = UUID.randomUUID().toString();
|
||||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||||
String scope = esiaConfig.getEsiaScopes();
|
String scope = esiaConfig.getEsiaScopes();
|
||||||
String scopeOrg = esiaConfig.getEsiaOrgScopes();
|
String scopeOrg = esiaConfig.getEsiaOrgScopes();
|
||||||
|
|
@ -189,7 +198,7 @@ public class EsiaAuthService {
|
||||||
parameters.put("scope", scope);
|
parameters.put("scope", scope);
|
||||||
parameters.put("scope_org", scopeOrg);
|
parameters.put("scope_org", scopeOrg);
|
||||||
parameters.put("timestamp", timestamp);
|
parameters.put("timestamp", timestamp);
|
||||||
parameters.put("state", state);
|
parameters.put("state", newState);
|
||||||
parameters.put("redirect_uri", redirectUrl);
|
parameters.put("redirect_uri", redirectUrl);
|
||||||
parameters.put("code", esiaAuthCode);
|
parameters.put("code", esiaAuthCode);
|
||||||
|
|
||||||
|
|
@ -202,7 +211,7 @@ public class EsiaAuthService {
|
||||||
.setParameter("code", esiaAuthCode)
|
.setParameter("code", esiaAuthCode)
|
||||||
.setParameter("grant_type", "authorization_code")
|
.setParameter("grant_type", "authorization_code")
|
||||||
.setParameter("client_secret", clientSecret)
|
.setParameter("client_secret", clientSecret)
|
||||||
.setParameter("state", state)
|
.setParameter("state", newState)
|
||||||
.setParameter("redirect_uri", redirectUrl)
|
.setParameter("redirect_uri", redirectUrl)
|
||||||
.setParameter("scope", scope)
|
.setParameter("scope", scope)
|
||||||
.setParameter("scope_org", scopeOrg)
|
.setParameter("scope_org", scopeOrg)
|
||||||
|
|
@ -231,6 +240,9 @@ public class EsiaAuthService {
|
||||||
tokenResponse != null ? tokenResponse.getErrorDescription() : "response is empty";
|
tokenResponse != null ? tokenResponse.getErrorDescription() : "response is empty";
|
||||||
throw new IllegalStateException("Esia response error. " + errMsg);
|
throw new IllegalStateException("Esia response error. " + errMsg);
|
||||||
}
|
}
|
||||||
|
if (!tokenResponse.getState().equals(newState)) {
|
||||||
|
throw new EsiaException("Token invalid. State from request not equals with state from response.");
|
||||||
|
}
|
||||||
esiaAccessTokenStr = tokenResponse.getAccessToken();
|
esiaAccessTokenStr = tokenResponse.getAccessToken();
|
||||||
String esiaRefreshTokenStr = tokenResponse.getRefreshToken();
|
String esiaRefreshTokenStr = tokenResponse.getRefreshToken();
|
||||||
startTime = System.currentTimeMillis();
|
startTime = System.currentTimeMillis();
|
||||||
|
|
@ -242,8 +254,8 @@ public class EsiaAuthService {
|
||||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
||||||
prnOid = esiaAccessToken.getSbjId();
|
prnOid = esiaAccessToken.getSbjId();
|
||||||
expiresIn = tokenResponse.getExpiresIn();
|
expiresIn = tokenResponse.getExpiresIn();
|
||||||
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
EsiaAuthInfoStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||||
EsiaTokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
EsiaAuthInfoStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new EsiaException(e);
|
throw new EsiaException(e);
|
||||||
|
|
@ -341,8 +353,8 @@ public class EsiaAuthService {
|
||||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
||||||
String prnOid = esiaAccessToken.getSbjId();
|
String prnOid = esiaAccessToken.getSbjId();
|
||||||
Long expiresIn = tokenResponse.getExpiresIn();
|
Long expiresIn = tokenResponse.getExpiresIn();
|
||||||
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
EsiaAuthInfoStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||||
EsiaTokensStore.addRefreshToken(prnOid, esiaNewRefreshToken, expiresIn);
|
EsiaAuthInfoStore.addRefreshToken(prnOid, esiaNewRefreshToken, expiresIn);
|
||||||
OrgInfo orgInfo = getOrgInfo(esiaAccessTokenStr);
|
OrgInfo orgInfo = getOrgInfo(esiaAccessTokenStr);
|
||||||
String ervuId = getErvuId(prnOid, orgInfo);
|
String ervuId = getErvuId(prnOid, orgInfo);
|
||||||
createTokenAndAddCookie(response, esiaAccessToken.getSbjId(), ervuId, true, expiresIn);
|
createTokenAndAddCookie(response, esiaAccessToken.getSbjId(), ervuId, true, expiresIn);
|
||||||
|
|
@ -390,11 +402,11 @@ public class EsiaAuthService {
|
||||||
String userId = null;
|
String userId = null;
|
||||||
try {
|
try {
|
||||||
userId = jwtTokenService.getUserAccountId(request);
|
userId = jwtTokenService.getUserAccountId(request);
|
||||||
String accessToken = EsiaTokensStore.getAccessToken(userId);
|
String accessToken = EsiaAuthInfoStore.getAccessToken(userId);
|
||||||
orgInfo = getOrgInfo(accessToken);
|
orgInfo = getOrgInfo(accessToken);
|
||||||
securityHelper.clearAccessCookies(response);
|
securityHelper.clearAccessCookies(response);
|
||||||
EsiaTokensStore.removeAccessToken(userId);
|
EsiaAuthInfoStore.removeAccessToken(userId);
|
||||||
EsiaTokensStore.removeRefreshToken(userId);
|
EsiaAuthInfoStore.removeRefreshToken(userId);
|
||||||
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
|
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
|
||||||
String redirectUrl = esiaConfig.getLogoutRedirectUrl();
|
String redirectUrl = esiaConfig.getLogoutRedirectUrl();
|
||||||
URL url = new URL(logoutUrl);
|
URL url = new URL(logoutUrl);
|
||||||
|
|
@ -506,13 +518,6 @@ public class EsiaAuthService {
|
||||||
return employee;
|
return employee;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMessageId(Exception exception) {
|
|
||||||
return Integer.toUnsignedString(Objects
|
|
||||||
.hashCode(getCurrentUsername()), 36)
|
|
||||||
+ "-"
|
|
||||||
+ Integer.toUnsignedString(exception.hashCode(), 36);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createTokenAndAddCookie(HttpServletResponse response, String userId, String ervuId,
|
private void createTokenAndAddCookie(HttpServletResponse response, String userId, String ervuId,
|
||||||
Boolean hasRole, Long expiresIn) {
|
Boolean hasRole, Long expiresIn) {
|
||||||
Token token = jwtTokenService.createAccessToken(userId, expiresIn, ervuId, hasRole);
|
Token token = jwtTokenService.createAccessToken(userId, expiresIn, ervuId, hasRole);
|
||||||
|
|
@ -532,6 +537,9 @@ public class EsiaAuthService {
|
||||||
if (!esiaHeader.getSbt().equals("access")) {
|
if (!esiaHeader.getSbt().equals("access")) {
|
||||||
return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid";
|
return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid";
|
||||||
}
|
}
|
||||||
|
if (!esiaHeader.getVer().equals(esiaConfig.getEsiaMarkerVer())) {
|
||||||
|
return "Token invalid. Token ver: " + esiaHeader.getVer() + " invalid";
|
||||||
|
}
|
||||||
if (!esiaHeader.getTyp().equals("JWT")) {
|
if (!esiaHeader.getTyp().equals("JWT")) {
|
||||||
return "Token invalid. Token type: " + esiaHeader.getTyp() + " invalid";
|
return "Token invalid. Token type: " + esiaHeader.getTyp() + " invalid";
|
||||||
}
|
}
|
||||||
|
|
@ -541,17 +549,16 @@ public class EsiaAuthService {
|
||||||
if (!esiaAccessToken.getIss().equals(esiaConfig.getEsiaIssuerUrl())) {
|
if (!esiaAccessToken.getIss().equals(esiaConfig.getEsiaIssuerUrl())) {
|
||||||
return "Token invalid. Token issuer:" + esiaAccessToken.getIss() + " invalid";
|
return "Token invalid. Token issuer:" + esiaAccessToken.getIss() + " invalid";
|
||||||
}
|
}
|
||||||
//TODO SUPPORT-8750
|
LocalDateTime iatTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getIat()),
|
||||||
// LocalDateTime iatTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getIat()),
|
ZoneId.systemDefault()
|
||||||
// ZoneId.systemDefault()
|
);
|
||||||
// );
|
LocalDateTime expTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getExp()),
|
||||||
// LocalDateTime expTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getExp()),
|
ZoneId.systemDefault()
|
||||||
// ZoneId.systemDefault()
|
);
|
||||||
// );
|
LocalDateTime currentTime = LocalDateTime.now();
|
||||||
// LocalDateTime currentTime = LocalDateTime.now();
|
if (!currentTime.isAfter(iatTime) || !expTime.isAfter(iatTime)) {
|
||||||
// if (!currentTime.isAfter(iatTime) || !expTime.isAfter(iatTime)) {
|
return "Token invalid. Token expired";
|
||||||
// return "Token invalid. Token expired";
|
}
|
||||||
// }
|
|
||||||
HttpResponse<String> response = signVerify(accessToken);
|
HttpResponse<String> response = signVerify(accessToken);
|
||||||
if (response.statusCode() != 200) {
|
if (response.statusCode() != 200) {
|
||||||
if (response.statusCode() == 401) {
|
if (response.statusCode() == 401) {
|
||||||
|
|
@ -579,4 +586,19 @@ public class EsiaAuthService {
|
||||||
throw new EsiaException(e);
|
throw new EsiaException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String verifyStateFromCookie(HttpServletRequest request, String state, HttpServletResponse response) {
|
||||||
|
Cookie cookie = WebUtils.getCookie(request, PRNS_UUID);
|
||||||
|
if (cookie == null) {
|
||||||
|
return "State invalid. Cookie not found";
|
||||||
|
}
|
||||||
|
String prnsUUID = cookie.getValue();
|
||||||
|
String oldState = EsiaAuthInfoStore.getState(prnsUUID);
|
||||||
|
if (oldState == null || !oldState.equals(state)) {
|
||||||
|
return "State invalid. State from ESIA not equals with state before";
|
||||||
|
}
|
||||||
|
EsiaAuthInfoStore.removeState(prnsUUID);
|
||||||
|
securityHelper.clearCookie(response, PRNS_UUID, "/");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package ru.micord.ervu.security.esia.token;
|
|
||||||
|
|
||||||
import net.javacrumbs.shedlock.core.SchedulerLock;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Eduard Tihomirov
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class TokensClearShedulerService {
|
|
||||||
@Scheduled(cron = "${esia.token.clear.cron:0 0 */1 * * *}")
|
|
||||||
@SchedulerLock(name = "clearToken")
|
|
||||||
@Transactional
|
|
||||||
public void load() {
|
|
||||||
EsiaTokensStore.removeExpiredRefreshToken();
|
|
||||||
EsiaTokensStore.removeExpiredAccessToken();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -48,7 +48,7 @@ public final class SecurityHelper {
|
||||||
addResponseCookie(response, emptyAuthMarker);
|
addResponseCookie(response, emptyAuthMarker);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addResponseCookie(HttpServletResponse response, ResponseCookie cookie) {
|
public void addResponseCookie(HttpServletResponse response, ResponseCookie cookie) {
|
||||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,4 +86,10 @@ public final class SecurityHelper {
|
||||||
.secure(accessCookieSecure)
|
.secure(accessCookieSecure)
|
||||||
.sameSite(accessCookieSameSite);
|
.sameSite(accessCookieSameSite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearCookie(HttpServletResponse response, String name, String path) {
|
||||||
|
ResponseCookie emptyCookie = createCookie(name, null, path)
|
||||||
|
.maxAge(0).build();
|
||||||
|
addResponseCookie(response, emptyCookie);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
|
||||||
import ru.micord.ervu.security.exception.UnauthorizedException;
|
import ru.micord.ervu.security.exception.UnauthorizedException;
|
||||||
|
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||||
|
|
||||||
import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils;
|
import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils;
|
||||||
|
|
@ -68,7 +68,7 @@ public class JwtTokenService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String[] ids = token.getUserAccountId().split(":");
|
String[] ids = token.getUserAccountId().split(":");
|
||||||
return EsiaTokensStore.validateAccessToken(ids[0]);
|
return EsiaAuthInfoStore.validateAccessToken(ids[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token getToken(String token) {
|
public Token getToken(String token) {
|
||||||
|
|
@ -81,11 +81,11 @@ public class JwtTokenService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAccessToken(HttpServletRequest request) {
|
public String getAccessToken(HttpServletRequest request) {
|
||||||
return EsiaTokensStore.getAccessToken(getUserAccountId(request));
|
return EsiaAuthInfoStore.getAccessToken(getUserAccountId(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRefreshToken(HttpServletRequest request) {
|
public String getRefreshToken(HttpServletRequest request) {
|
||||||
return EsiaTokensStore.getRefreshToken(getUserAccountId(request));
|
return EsiaAuthInfoStore.getRefreshToken(getUserAccountId(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserAccountId(HttpServletRequest request) {
|
public String getUserAccountId(HttpServletRequest request) {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ ESIA_REDIRECT_URL=https://lkrp-dev.micord.ru/ul/
|
||||||
ESIA_LOGOUT_REDIRECT_URL=https://lkrp-dev.micord.ru/ul/home.html
|
ESIA_LOGOUT_REDIRECT_URL=https://lkrp-dev.micord.ru/ul/home.html
|
||||||
ESIA_UPLOAD_DATA_ROLE=MNSV89_UPLOAD_DATA
|
ESIA_UPLOAD_DATA_ROLE=MNSV89_UPLOAD_DATA
|
||||||
ESIA_CLIENT_CERT_HASH=04508B4B0B58776A954A0E15F574B4E58799D74C61EE020B3330716C203E3BDD
|
ESIA_CLIENT_CERT_HASH=04508B4B0B58776A954A0E15F574B4E58799D74C61EE020B3330716C203E3BDD
|
||||||
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
|
ESIA_AUTH_INFO_CLEAR_CRON=0 0 */1 * * *
|
||||||
|
|
||||||
|
|
||||||
SIGN_URL=https://ervu-sign-dev.k8s.micord.ru/sign
|
SIGN_URL=https://ervu-sign-dev.k8s.micord.ru/sign
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
<property name="av.kafka.group.id" value="1"/>
|
<property name="av.kafka.group.id" value="1"/>
|
||||||
<property name="av.kafka.download.response" value="ervu.lkrp.av-fileupload-status"/>
|
<property name="av.kafka.download.response" value="ervu.lkrp.av-fileupload-status"/>
|
||||||
<property name="sign.verify.url" value="https://ervu-sign-dev.k8s.micord.ru/verify"/>
|
<property name="sign.verify.url" value="https://ervu-sign-dev.k8s.micord.ru/verify"/>
|
||||||
<property name="esia.token.clear.cron" value="0 0 */1 * * *"/>
|
<property name="esia.auth.info.clear.cron" value="0 0 */1 * * *"/>
|
||||||
<property name="esia.upload.data.role" value="MNSV89_UPLOAD_DATA"/>
|
<property name="esia.upload.data.role" value="MNSV89_UPLOAD_DATA"/>
|
||||||
<property name="webdav.cleanup.cron" value="0 0 0 * * *"/>
|
<property name="webdav.cleanup.cron" value="0 0 0 * * *"/>
|
||||||
<property name="webdav.retry.delay" value="500"/>
|
<property name="webdav.retry.delay" value="500"/>
|
||||||
|
|
|
||||||
|
|
@ -42,33 +42,14 @@ export abstract class AuthGuard implements CanActivate {
|
||||||
}).then((isAccess) => {
|
}).then((isAccess) => {
|
||||||
let params = new URLSearchParams(url.search);
|
let params = new URLSearchParams(url.search);
|
||||||
let code = params.get('code');
|
let code = params.get('code');
|
||||||
|
let state = params.get('state');
|
||||||
let error = params.get('error');
|
let error = params.get('error');
|
||||||
let errorDescription = params.get('error_description');
|
let errorDescription = params.get('error_description');
|
||||||
if (isAccess) {
|
if (isAccess) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (code || error) {
|
|
||||||
const params = new HttpParams().set('code', code).set('error', error);
|
|
||||||
this.httpClient.get("esia/auth",
|
|
||||||
{
|
|
||||||
params: params,
|
|
||||||
responseType: 'text',
|
|
||||||
observe: 'response',
|
|
||||||
headers: {
|
|
||||||
"Error-intercept-skip": "true"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.toPromise()
|
|
||||||
.then(
|
|
||||||
(response) => {
|
|
||||||
window.open(url.origin + url.pathname, "_self");
|
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
let errorMessage = reason.error.messages != null
|
|
||||||
? reason.error.messages
|
|
||||||
: reason.error.replaceAll('\\', '');
|
|
||||||
if (error) {
|
if (error) {
|
||||||
errorMessage =
|
let errorMessage =
|
||||||
'Произошла неизвестная ошибка. Обратитесь к системному администратору';
|
'Произошла неизвестная ошибка. Обратитесь к системному администратору';
|
||||||
let errorCode = this.extractCode(errorDescription);
|
let errorCode = this.extractCode(errorDescription);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
|
|
@ -78,10 +59,18 @@ export abstract class AuthGuard implements CanActivate {
|
||||||
this.messageService.error(errorMessage);
|
this.messageService.error(errorMessage);
|
||||||
console.error(consoleError);
|
console.error(consoleError);
|
||||||
}
|
}
|
||||||
else {
|
if (code && state) {
|
||||||
this.messageService.error(errorMessage);
|
const params = new HttpParams().set('code', code).set('state', state);
|
||||||
console.error(reason);
|
this.httpClient.get("esia/auth",
|
||||||
}
|
{
|
||||||
|
params: params,
|
||||||
|
responseType: 'text',
|
||||||
|
observe: 'response',
|
||||||
|
})
|
||||||
|
.toPromise()
|
||||||
|
.then(
|
||||||
|
() => {
|
||||||
|
window.open(url.origin + url.pathname, "_self");
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue