Merge branch 'develop' of 10.10.31.70:/ervu-lkrp-fl into develop
This commit is contained in:
commit
853bacce04
6 changed files with 57 additions and 43 deletions
|
|
@ -2,4 +2,7 @@ package ru.micord.ervu.security;
|
||||||
|
|
||||||
public class SecurityConstants {
|
public class SecurityConstants {
|
||||||
public static final String ESIA_LOGOUT = "/esia/logout";
|
public static final String ESIA_LOGOUT = "/esia/logout";
|
||||||
|
public static final String AUTH_TOKEN = "auth_token";
|
||||||
|
public static final String AUTH_MARKER = "webbpm.ervu-lkrp-fl";
|
||||||
|
public static final String PRNS_UUID = "prns_uuid_fl";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.support.MessageSourceAccessor;
|
import org.springframework.context.support.MessageSourceAccessor;
|
||||||
|
import ru.micord.ervu.security.esia.exception.EsiaException;
|
||||||
import ru.micord.ervu.security.esia.model.ExpiringState;
|
import ru.micord.ervu.security.esia.model.ExpiringState;
|
||||||
import ru.micord.ervu.security.esia.model.ExpiringToken;
|
import ru.micord.ervu.security.esia.model.ExpiringToken;
|
||||||
|
|
||||||
|
|
@ -105,19 +106,24 @@ public class EsiaAuthInfoStore {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean containsState(String prnsUUID, String state) {
|
public static void validateState(String prnsUUID, String state) {
|
||||||
List<ExpiringState> states = PRNS_UUID_STATE_MAP.get(prnsUUID);
|
List<ExpiringState> states = PRNS_UUID_STATE_MAP.get(prnsUUID);
|
||||||
if (states == null) {
|
if (states == null) {
|
||||||
return false;
|
throw new EsiaException("State invalid. No state found");
|
||||||
}
|
}
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
states.removeIf(expiringState -> expiringState.getExpiryTime() < currentTime);
|
|
||||||
|
StringBuilder statesStringBuilder = new StringBuilder();
|
||||||
for (ExpiringState expiringState : states) {
|
for (ExpiringState expiringState : states) {
|
||||||
if (expiringState.getState().equals(state)) {
|
if (expiringState.getState().equals(state)) {
|
||||||
return true;
|
if (expiringState.getExpiryTime() < currentTime) {
|
||||||
|
throw new EsiaException("State invalid. State : " + state + " expired at : " + expiringState.getExpiryTime());
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
statesStringBuilder.append(expiringState.getState(), 0, 8).append(", ");
|
||||||
}
|
}
|
||||||
return false;
|
throw new EsiaException("State invalid. Backend states :" + statesStringBuilder + " cookie state :" + state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeState(String prnsUUID) {
|
public static void removeState(String prnsUUID) {
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,6 @@ import org.springframework.security.core.context.SecurityContext;
|
||||||
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 org.springframework.web.util.WebUtils;
|
||||||
import ru.micord.ervu.audit.constants.AuditConstants;
|
|
||||||
import ru.micord.ervu.audit.service.AuditService;
|
|
||||||
import ru.micord.ervu.kafka.model.Document;
|
import ru.micord.ervu.kafka.model.Document;
|
||||||
import ru.micord.ervu.kafka.model.Person;
|
import ru.micord.ervu.kafka.model.Person;
|
||||||
import ru.micord.ervu.kafka.model.Response;
|
import ru.micord.ervu.kafka.model.Response;
|
||||||
|
|
@ -46,6 +44,7 @@ import ru.micord.ervu.security.esia.model.EsiaHeader;
|
||||||
import ru.micord.ervu.security.esia.model.EsiaTokenResponse;
|
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.PersonModel;
|
import ru.micord.ervu.security.esia.model.PersonModel;
|
||||||
|
import ru.micord.ervu.security.esia.model.SignResponse;
|
||||||
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||||
import ru.micord.ervu.security.esia.config.EsiaConfig;
|
import ru.micord.ervu.security.esia.config.EsiaConfig;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -60,8 +59,7 @@ import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||||
|
|
||||||
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.getCurrentUserEsiaId;
|
import static ru.micord.ervu.security.SecurityConstants.PRNS_UUID;
|
||||||
import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eduard Tihomirov
|
* @author Eduard Tihomirov
|
||||||
|
|
@ -71,7 +69,6 @@ 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;
|
||||||
|
|
||||||
|
|
@ -106,7 +103,6 @@ public class EsiaAuthService {
|
||||||
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 prnsUUID = UUID.randomUUID().toString();
|
String prnsUUID = UUID.randomUUID().toString();
|
||||||
Cookie oldPrnsCookie = WebUtils.getCookie(request, PRNS_UUID);
|
Cookie oldPrnsCookie = WebUtils.getCookie(request, PRNS_UUID);
|
||||||
if (oldPrnsCookie != null) {
|
if (oldPrnsCookie != null) {
|
||||||
|
|
@ -121,12 +117,14 @@ public class EsiaAuthService {
|
||||||
parameters.put("client_id", clientId);
|
parameters.put("client_id", clientId);
|
||||||
parameters.put("scope", scope);
|
parameters.put("scope", scope);
|
||||||
parameters.put("timestamp", timestamp);
|
parameters.put("timestamp", timestamp);
|
||||||
parameters.put("state", state);
|
parameters.put("state", "%s");
|
||||||
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
||||||
|
|
||||||
String clientSecret = signMap(parameters);
|
SignResponse signResponse = signMap(parameters);
|
||||||
|
String state = signResponse.getState();
|
||||||
|
String clientSecret = signResponse.getSignature();
|
||||||
EsiaAuthInfoStore.addState(prnsUUID, state, esiaConfig.getEsiaStateCookieLifeTime(), esiaConfig.getEsiaLoginAttemptsCount());
|
EsiaAuthInfoStore.addState(prnsUUID, state, esiaConfig.getEsiaStateCookieLifeTime(), esiaConfig.getEsiaLoginAttemptsCount());
|
||||||
ResponseCookie prnsCookie = securityHelper.createCookie(PRNS_UUID, prnsUUID, "/")
|
ResponseCookie prnsCookie = securityHelper.createAccessCookie(PRNS_UUID, prnsUUID)
|
||||||
.maxAge(esiaConfig.getEsiaStateCookieLifeTime())
|
.maxAge(esiaConfig.getEsiaStateCookieLifeTime())
|
||||||
.build();
|
.build();
|
||||||
securityHelper.addResponseCookie(response, prnsCookie);
|
securityHelper.addResponseCookie(response, prnsCookie);
|
||||||
|
|
@ -183,16 +181,12 @@ public class EsiaAuthService {
|
||||||
String prnOid = null;
|
String prnOid = null;
|
||||||
Long expiresIn = null;
|
Long expiresIn = null;
|
||||||
long signSecret = 0, requestAccessToken = 0, verifySecret = 0;
|
long signSecret = 0, requestAccessToken = 0, verifySecret = 0;
|
||||||
String verifyStateResult = verifyStateFromCookie(request, state, response);
|
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 newState = UUID.randomUUID().toString();
|
|
||||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||||
String scope = esiaConfig.getEsiaScopes();
|
String scope = esiaConfig.getEsiaScopes();
|
||||||
|
|
||||||
|
|
@ -200,13 +194,14 @@ public class EsiaAuthService {
|
||||||
parameters.put("client_id", clientId);
|
parameters.put("client_id", clientId);
|
||||||
parameters.put("scope", scope);
|
parameters.put("scope", scope);
|
||||||
parameters.put("timestamp", timestamp);
|
parameters.put("timestamp", timestamp);
|
||||||
parameters.put("state", newState);
|
parameters.put("state", "%s");
|
||||||
parameters.put("redirect_uri", redirectUrl);
|
parameters.put("redirect_uri", redirectUrl);
|
||||||
parameters.put("code", esiaAuthCode);
|
parameters.put("code", esiaAuthCode);
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
String clientSecret = signMap(parameters);
|
SignResponse signResponse = signMap(parameters);
|
||||||
signSecret = System.currentTimeMillis() - startTime;
|
signSecret = System.currentTimeMillis() - startTime;
|
||||||
|
String newState = signResponse.getState();
|
||||||
|
String clientSecret = signResponse.getSignature();
|
||||||
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaTokenUrl();
|
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaTokenUrl();
|
||||||
String postBody = new FormUrlencoded()
|
String postBody = new FormUrlencoded()
|
||||||
.setParameter("client_id", clientId)
|
.setParameter("client_id", clientId)
|
||||||
|
|
@ -296,18 +291,19 @@ public class EsiaAuthService {
|
||||||
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 redirectUrl = esiaConfig.getRedirectUrl();
|
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||||
|
|
||||||
Map<String, String> parameters = new LinkedHashMap<String, String>();
|
Map<String, String> parameters = new LinkedHashMap<String, String>();
|
||||||
parameters.put("client_id", clientId);
|
parameters.put("client_id", clientId);
|
||||||
parameters.put("scope", esiaConfig.getEsiaScopes());
|
parameters.put("scope", esiaConfig.getEsiaScopes());
|
||||||
parameters.put("timestamp", timestamp);
|
parameters.put("timestamp", timestamp);
|
||||||
parameters.put("state", state);
|
parameters.put("state", "%s");
|
||||||
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
||||||
parameters.put("refresh_token", refreshToken);
|
parameters.put("refresh_token", refreshToken);
|
||||||
|
|
||||||
String clientSecret = signMap(parameters);
|
SignResponse signResponse = signMap(parameters);
|
||||||
|
String state = signResponse.getState();
|
||||||
|
String clientSecret = signResponse.getSignature();
|
||||||
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaTokenUrl();
|
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaTokenUrl();
|
||||||
String postBody = new FormUrlencoded()
|
String postBody = new FormUrlencoded()
|
||||||
.setParameter("client_id", clientId)
|
.setParameter("client_id", clientId)
|
||||||
|
|
@ -360,7 +356,7 @@ public class EsiaAuthService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String signMap(Map<String, String> paramsToSign) {
|
private SignResponse signMap(Map<String, String> paramsToSign) {
|
||||||
try {
|
try {
|
||||||
StringBuilder toSign = new StringBuilder();
|
StringBuilder toSign = new StringBuilder();
|
||||||
for (String s : paramsToSign.values()) {
|
for (String s : paramsToSign.values()) {
|
||||||
|
|
@ -379,7 +375,7 @@ public class EsiaAuthService {
|
||||||
.build()
|
.build()
|
||||||
.send(request, HttpResponse.BodyHandlers.ofString());
|
.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
errorHandler(response);
|
errorHandler(response);
|
||||||
return response.body();
|
return objectMapper.readValue(response.body(), SignResponse.class);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
|
@ -521,17 +517,18 @@ public class EsiaAuthService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String verifyStateFromCookie(HttpServletRequest request, String state, HttpServletResponse response) {
|
private void verifyStateFromCookie(HttpServletRequest request, String state, HttpServletResponse response) {
|
||||||
Cookie cookie = WebUtils.getCookie(request, PRNS_UUID);
|
Cookie cookie = WebUtils.getCookie(request, PRNS_UUID);
|
||||||
if (cookie == null) {
|
if (cookie == null) {
|
||||||
return "State invalid. Cookie not found";
|
throw new EsiaException("State invalid. Cookie not found");
|
||||||
}
|
}
|
||||||
String prnsUUID = cookie.getValue();
|
String prnsUUID = cookie.getValue();
|
||||||
if (!EsiaAuthInfoStore.containsState(prnsUUID, state)) {
|
try {
|
||||||
return "State invalid. State from ESIA not equals with state before";
|
EsiaAuthInfoStore.validateState(prnsUUID, state);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
EsiaAuthInfoStore.removeState(prnsUUID);
|
||||||
|
securityHelper.clearAccessCookie(response, PRNS_UUID);
|
||||||
}
|
}
|
||||||
EsiaAuthInfoStore.removeState(prnsUUID);
|
|
||||||
securityHelper.clearCookie(response, PRNS_UUID, "/");
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@ import org.springframework.web.context.request.RequestAttributes;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
|
||||||
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
|
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
|
||||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.AUTH_MARKER;
|
import static ru.micord.ervu.security.SecurityConstants.AUTH_MARKER;
|
||||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.AUTH_TOKEN;
|
import static ru.micord.ervu.security.SecurityConstants.AUTH_TOKEN;
|
||||||
|
import static ru.micord.ervu.security.SecurityConstants.PRNS_UUID;
|
||||||
|
|
||||||
public final class SecurityHelper {
|
public final class SecurityHelper {
|
||||||
@Value("${cookie.path:#{null}}")
|
@Value("${cookie.path:#{null}}")
|
||||||
|
|
@ -36,9 +37,7 @@ public final class SecurityHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearAccessCookies(HttpServletResponse response) {
|
public void clearAccessCookies(HttpServletResponse response) {
|
||||||
ResponseCookie emptyAuthToken = createCookie(AUTH_TOKEN, null, accessCookiePath)
|
clearCookie(response, AUTH_TOKEN, accessCookiePath);
|
||||||
.maxAge(0).build();
|
|
||||||
addResponseCookie(response, emptyAuthToken);
|
|
||||||
|
|
||||||
ResponseCookie emptyAuthMarker = createCookie(AUTH_MARKER, null, "/")
|
ResponseCookie emptyAuthMarker = createCookie(AUTH_MARKER, null, "/")
|
||||||
.maxAge(0)
|
.maxAge(0)
|
||||||
|
|
@ -46,6 +45,7 @@ public final class SecurityHelper {
|
||||||
.httpOnly(false)
|
.httpOnly(false)
|
||||||
.build();
|
.build();
|
||||||
addResponseCookie(response, emptyAuthMarker);
|
addResponseCookie(response, emptyAuthMarker);
|
||||||
|
clearCookie(response, PRNS_UUID, accessCookiePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCookie(HttpServletResponse response, String name, String path) {
|
public void clearCookie(HttpServletResponse response, String name, String path) {
|
||||||
|
|
@ -92,4 +92,12 @@ public final class SecurityHelper {
|
||||||
.secure(accessCookieSecure)
|
.secure(accessCookieSecure)
|
||||||
.sameSite(accessCookieSameSite);
|
.sameSite(accessCookieSameSite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResponseCookie.ResponseCookieBuilder createAccessCookie(String name, String value) {
|
||||||
|
return createCookie(name, value, accessCookiePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAccessCookie(HttpServletResponse response, String name) {
|
||||||
|
clearCookie(response, name, accessCookiePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,9 @@ import org.springframework.web.util.WebUtils;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
|
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||||
|
|
||||||
public final class SecurityUtil {
|
import static ru.micord.ervu.security.SecurityConstants.AUTH_TOKEN;
|
||||||
public static final String AUTH_TOKEN = "auth_token";
|
|
||||||
|
|
||||||
public static final String AUTH_MARKER = "webbpm.ervu-lkrp-fl";
|
public final class SecurityUtil {
|
||||||
|
|
||||||
private SecurityUtil() {
|
private SecurityUtil() {
|
||||||
//empty
|
//empty
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import {Injectable} from "@angular/core";
|
||||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
|
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
import {HttpClient, HttpParams} from "@angular/common/http";
|
import {HttpClient, HttpParams} from "@angular/common/http";
|
||||||
import {MessagesService} from "@webbpm/base-package";
|
import {MessagesService, ProgressIndicationService} from "@webbpm/base-package";
|
||||||
import {AuthenticationService} from "../authentication.service";
|
import {AuthenticationService} from "../authentication.service";
|
||||||
import {EsiaErrorDetail} from "../EsiaErrorDetail";
|
import {EsiaErrorDetail} from "../EsiaErrorDetail";
|
||||||
|
|
||||||
|
|
@ -13,6 +13,7 @@ export abstract class AuthGuard implements CanActivate {
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
private httpClient: HttpClient,
|
private httpClient: HttpClient,
|
||||||
private authenticationService: AuthenticationService,
|
private authenticationService: AuthenticationService,
|
||||||
|
private progressIndicationService: ProgressIndicationService,
|
||||||
private messageService: MessagesService
|
private messageService: MessagesService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue