Merge branch 'feature/SUPPORT-8755_new' into hotfix/1.9.3
This commit is contained in:
commit
c5baf9028f
7 changed files with 62 additions and 48 deletions
|
|
@ -25,7 +25,6 @@ import ru.micord.ervu.security.webbpm.jwt.JwtMatcher;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.UnauthorizedEntryPoint;
|
import ru.micord.ervu.security.webbpm.jwt.UnauthorizedEntryPoint;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.filter.JwtAuthenticationFilter;
|
import ru.micord.ervu.security.webbpm.jwt.filter.JwtAuthenticationFilter;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
|
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
|
||||||
|
|
||||||
import static ru.micord.ervu.security.SecurityConstants.ESIA_LOGOUT;
|
import static ru.micord.ervu.security.SecurityConstants.ESIA_LOGOUT;
|
||||||
|
|
||||||
|
|
@ -105,10 +104,9 @@ public class SecurityConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JwtAuthenticationFilter jwtAuthenticationFilter(SecurityHelper securityHelper,
|
public JwtAuthenticationFilter jwtAuthenticationFilter(SecurityHelper securityHelper,
|
||||||
AuthenticationManager manager,
|
AuthenticationManager manager) {
|
||||||
JwtTokenService jwtTokenService) {
|
|
||||||
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(
|
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(
|
||||||
new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper, jwtTokenService);
|
new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper);
|
||||||
jwtAuthenticationFilter.setAuthenticationManager(manager);
|
jwtAuthenticationFilter.setAuthenticationManager(manager);
|
||||||
return jwtAuthenticationFilter;
|
return jwtAuthenticationFilter;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ 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;
|
||||||
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
|
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
|
||||||
import ru.micord.ervu.security.esia.token.TokensStore;
|
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||||
import ru.micord.ervu.security.esia.config.EsiaConfig;
|
import ru.micord.ervu.security.esia.config.EsiaConfig;
|
||||||
import ru.micord.ervu.security.esia.model.FormUrlencoded;
|
import ru.micord.ervu.security.esia.model.FormUrlencoded;
|
||||||
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
||||||
|
|
@ -199,8 +199,8 @@ public class EsiaAuthService {
|
||||||
EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr);
|
EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr);
|
||||||
String prnOid = esiaAccessToken.getSbj_id();
|
String prnOid = esiaAccessToken.getSbj_id();
|
||||||
Long expiresIn = tokenResponse.getExpires_in();
|
Long expiresIn = tokenResponse.getExpires_in();
|
||||||
TokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||||
TokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
EsiaTokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
||||||
Response ervuIdResponse = getErvuIdResponse(esiaAccessTokenStr);
|
Response ervuIdResponse = getErvuIdResponse(esiaAccessTokenStr);
|
||||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId());
|
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId());
|
||||||
int expiry = tokenResponse.getExpires_in().intValue();
|
int expiry = tokenResponse.getExpires_in().intValue();
|
||||||
|
|
@ -276,8 +276,8 @@ public class EsiaAuthService {
|
||||||
EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr);
|
EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr);
|
||||||
String prnOid = esiaAccessToken.getSbj_id();
|
String prnOid = esiaAccessToken.getSbj_id();
|
||||||
Long expiresIn = tokenResponse.getExpires_in();
|
Long expiresIn = tokenResponse.getExpires_in();
|
||||||
TokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||||
TokensStore.addRefreshToken(prnOid, esiaNewRefreshTokenStr, expiresIn);
|
EsiaTokensStore.addRefreshToken(prnOid, esiaNewRefreshTokenStr, expiresIn);
|
||||||
Response ervuIdResponse = getErvuIdResponse(esiaAccessTokenStr);
|
Response ervuIdResponse = getErvuIdResponse(esiaAccessTokenStr);
|
||||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId());
|
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId());
|
||||||
int expiry = tokenResponse.getExpires_in().intValue();
|
int expiry = tokenResponse.getExpires_in().intValue();
|
||||||
|
|
@ -335,8 +335,8 @@ public class EsiaAuthService {
|
||||||
try {
|
try {
|
||||||
securityHelper.clearAccessCookies(response);
|
securityHelper.clearAccessCookies(response);
|
||||||
String userId = jwtTokenService.getUserAccountId(request);
|
String userId = jwtTokenService.getUserAccountId(request);
|
||||||
TokensStore.removeAccessToken(userId);
|
EsiaTokensStore.removeAccessToken(userId);
|
||||||
TokensStore.removeRefreshToken(userId);
|
EsiaTokensStore.removeRefreshToken(userId);
|
||||||
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
|
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
|
||||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||||
URL url = new URL(logoutUrl);
|
URL url = new URL(logoutUrl);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
package ru.micord.ervu.security.esia.token;
|
package ru.micord.ervu.security.esia.token;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eduard Tihomirov
|
* @author Eduard Tihomirov
|
||||||
*/
|
*/
|
||||||
public class TokensStore {
|
public class EsiaTokensStore {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
private static final Map<String, ExpiringToken> accessTokensMap = new ConcurrentHashMap<>();
|
private static final Map<String, ExpiringToken> accessTokensMap = new ConcurrentHashMap<>();
|
||||||
private static final Map<String, ExpiringToken> refreshTokensMap = new ConcurrentHashMap<>();
|
private static final Map<String, ExpiringToken> refreshTokensMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
@ -21,6 +26,19 @@ public class TokensStore {
|
||||||
return accessTokensMap.get(prnOid).getAccessToken();
|
return accessTokensMap.get(prnOid).getAccessToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean validateAccessToken(String prnOid) {
|
||||||
|
ExpiringToken token = accessTokensMap.get(prnOid);
|
||||||
|
if (token == null || token.getAccessToken() == null) {
|
||||||
|
LOGGER.error("No ESIA access token for prnOid: " + prnOid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (token.isExpired()) {
|
||||||
|
LOGGER.error("ESIA access token expired for prnOid: " + prnOid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static void removeExpiredAccessToken() {
|
public static void removeExpiredAccessToken() {
|
||||||
for (String key : accessTokensMap.keySet()) {
|
for (String key : accessTokensMap.keySet()) {
|
||||||
ExpiringToken token = accessTokensMap.get(key);
|
ExpiringToken token = accessTokensMap.get(key);
|
||||||
|
|
@ -14,7 +14,7 @@ public class TokensClearShedulerService {
|
||||||
@SchedulerLock(name = "clearToken")
|
@SchedulerLock(name = "clearToken")
|
||||||
@Transactional
|
@Transactional
|
||||||
public void load() {
|
public void load() {
|
||||||
TokensStore.removeExpiredRefreshToken();
|
EsiaTokensStore.removeExpiredRefreshToken();
|
||||||
TokensStore.removeExpiredAccessToken();
|
EsiaTokensStore.removeExpiredAccessToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package ru.micord.ervu.security.webbpm.jwt;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import io.jsonwebtoken.ExpiredJwtException;
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
|
@ -11,9 +13,13 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
||||||
|
|
||||||
|
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtAuthenticationProvider implements AuthenticationProvider {
|
public class JwtAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
|
@ -42,16 +48,26 @@ public class JwtAuthenticationProvider implements AuthenticationProvider {
|
||||||
throw new BadCredentialsException("Authentication Failed.", e);
|
throw new BadCredentialsException("Authentication Failed.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jwtTokenService.isValid(token)) {
|
if (jwtTokenService.isValid(token)) {
|
||||||
throw new BadCredentialsException("Auth token is not valid for user " + token.getUserAccountId());
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(
|
||||||
|
REFERENCE_REQUEST);
|
||||||
|
String[] ids = token.getUserAccountId().split(":");
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalStateException("No request found in request attributes");
|
||||||
|
}
|
||||||
|
if (request.getRequestURI().endsWith("esia/logout") || ids.length == 2) {
|
||||||
|
UsernamePasswordAuthenticationToken pwdToken =
|
||||||
|
UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null,
|
||||||
|
Collections.emptyList()
|
||||||
|
);
|
||||||
|
|
||||||
|
return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UsernamePasswordAuthenticationToken pwdToken =
|
throw new BadCredentialsException(
|
||||||
UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null,
|
"Auth token is not valid for user " + token.getUserAccountId());
|
||||||
Collections.emptyList()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
|
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
|
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
|
||||||
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||||
|
|
@ -35,16 +36,12 @@ public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||||
|
|
||||||
private final SecurityHelper securityHelper;
|
private final SecurityHelper securityHelper;
|
||||||
|
|
||||||
private final JwtTokenService jwtTokenService;
|
|
||||||
|
|
||||||
public JwtAuthenticationFilter(RequestMatcher requestMatcher,
|
public JwtAuthenticationFilter(RequestMatcher requestMatcher,
|
||||||
AuthenticationEntryPoint entryPoint,
|
AuthenticationEntryPoint entryPoint,
|
||||||
SecurityHelper securityHelper,
|
SecurityHelper securityHelper) {
|
||||||
JwtTokenService jwtTokenService) {
|
|
||||||
super(requestMatcher);
|
super(requestMatcher);
|
||||||
this.entryPoint = entryPoint;
|
this.entryPoint = entryPoint;
|
||||||
this.securityHelper = securityHelper;
|
this.securityHelper = securityHelper;
|
||||||
this.jwtTokenService = jwtTokenService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -58,18 +55,11 @@ public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
authentication = getAuthenticationManager().authenticate(authentication);
|
authentication = getAuthenticationManager().authenticate(authentication);
|
||||||
if (!httpServletRequest.getRequestURI().endsWith("esia/logout")) {
|
|
||||||
Token token = jwtTokenService.getToken(tokenStr);
|
|
||||||
String[] ids = token.getUserAccountId().split(":");
|
|
||||||
if (ids.length != 2) {
|
|
||||||
throw new CredentialsExpiredException("Invalid token. User has no ervuId");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (CredentialsExpiredException e) {
|
catch (AuthenticationException e) {
|
||||||
|
LOGGER.warn(e.getMessage());
|
||||||
securityHelper.clearAccessCookies(httpServletResponse);
|
securityHelper.clearAccessCookies(httpServletResponse);
|
||||||
httpServletResponse.setStatus(401);
|
httpServletResponse.setStatus(401);
|
||||||
LOGGER.warn(e.getMessage());
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return authentication;
|
return authentication;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ 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.TokensStore;
|
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||||
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;
|
||||||
|
|
@ -34,9 +34,6 @@ public class JwtTokenService {
|
||||||
ResourceMetadataUtils.PROJECT_GROUP_ID + "." + ResourceMetadataUtils.PROJECT_ARTIFACT_ID;
|
ResourceMetadataUtils.PROJECT_GROUP_ID + "." + ResourceMetadataUtils.PROJECT_ARTIFACT_ID;
|
||||||
private final SecretKey SIGNING_KEY;
|
private final SecretKey SIGNING_KEY;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private HttpServletRequest request;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public JwtTokenService(@Value("${webbpm.security.token.secret.key:ZjE5ZjMxNmYtODViZC00ZTQ5LWIxZmYtOGEzYzE3Yjc1MDVk}")
|
public JwtTokenService(@Value("${webbpm.security.token.secret.key:ZjE5ZjMxNmYtODViZC00ZTQ5LWIxZmYtOGEzYzE3Yjc1MDVk}")
|
||||||
String secretKey) {
|
String secretKey) {
|
||||||
|
|
@ -67,7 +64,7 @@ public class JwtTokenService {
|
||||||
LOGGER.info("Token {} is expired ", token.getValue());
|
LOGGER.info("Token {} is expired ", token.getValue());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return EsiaTokensStore.validateAccessToken(token.getUserAccountId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token getToken(String token) {
|
public Token getToken(String token) {
|
||||||
|
|
@ -79,17 +76,12 @@ public class JwtTokenService {
|
||||||
return new Token(claims.getSubject(), claims.getIssuer(), claims.getExpiration(), token);
|
return new Token(claims.getSubject(), claims.getIssuer(), claims.getExpiration(), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getErvuId() {
|
|
||||||
String extractAuthToken = extractAuthToken(request);
|
|
||||||
return getToken(extractAuthToken).getUserAccountId().split(":")[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAccessToken(HttpServletRequest request) {
|
public String getAccessToken(HttpServletRequest request) {
|
||||||
return TokensStore.getAccessToken(getUserAccountId(request));
|
return EsiaTokensStore.getAccessToken(getUserAccountId(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRefreshToken(HttpServletRequest request) {
|
public String getRefreshToken(HttpServletRequest request) {
|
||||||
return TokensStore.getRefreshToken(getUserAccountId(request));
|
return EsiaTokensStore.getRefreshToken(getUserAccountId(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserAccountId(HttpServletRequest request) {
|
public String getUserAccountId(HttpServletRequest request) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue