SUPPORT-8609 fix

This commit is contained in:
kochetkov 2024-11-20 10:29:24 +03:00
parent d26799c630
commit 7f1793262d
3 changed files with 105 additions and 25 deletions

View file

@ -13,6 +13,7 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
@ -49,31 +50,22 @@ public class SecurityConfig {
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
httpConfigure(http);
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new RequestContextFilter(), LogoutFilter.class);
http.addFilterAfter(filterChainExceptionHandler, RequestContextFilter.class);
return http.build();
}
protected void httpConfigure(HttpSecurity httpSecurity) throws Exception {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
tokenRepository.setCookieName(TokenConstants.CSRF_TOKEN_NAME);
tokenRepository.setHeaderName(TokenConstants.CSRF_HEADER_NAME);
tokenRepository.setCookiePath("/");
public SecurityFilterChain filterChain(HttpSecurity http,
CookieCsrfTokenRepository tokenRepository)
throws Exception {
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
delegate.setCsrfRequestAttributeName(null);
// Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
// default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
CsrfTokenRequestHandler requestHandler = delegate::handle;
httpSecurity.authorizeHttpRequests(
http.authorizeHttpRequests(
(authorizeHttpRequests) -> authorizeHttpRequests.requestMatchers(PERMIT_ALL)
.permitAll()
.anyRequest()
.authenticated())
.csrf((csrf) -> csrf.csrfTokenRepository(tokenRepository)
.csrfTokenRequestHandler(requestHandler))
.csrfTokenRequestHandler(requestHandler)
.sessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy()))
.logout((logout) -> logout.logoutUrl(ESIA_LOGOUT)
.logoutSuccessHandler(new LogoutSuccessHandler(tokenRepository, esiaAuthService)))
.exceptionHandling()
@ -81,6 +73,19 @@ public class SecurityConfig {
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new RequestContextFilter(), LogoutFilter.class);
http.addFilterAfter(filterChainExceptionHandler, RequestContextFilter.class);
return http.build();
}
@Bean
public CookieCsrfTokenRepository cookieCsrfTokenRepository() {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
tokenRepository.setCookieName(TokenConstants.CSRF_TOKEN_NAME);
tokenRepository.setHeaderName(TokenConstants.CSRF_HEADER_NAME);
tokenRepository.setCookiePath("/");
return tokenRepository;
}
public AuthenticationEntryPoint entryPoint() {

View file

@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import ervu.service.okopf.OkopfService;
import org.springframework.security.authentication.AuthenticationManager;
import ru.micord.ervu.security.esia.token.TokensStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -50,7 +51,6 @@ import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.micord.ervu.security.webbpm.jwt.model.Token;
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
/**
* @author Eduard Tihomirov
@ -72,6 +72,8 @@ public class EsiaAuthService {
private OkopfService okopfService;
@Autowired
private SecurityHelper securityHelper;
@Autowired
private AuthenticationManager authenticationManager;
@Value("${ervu.kafka.org.reply.topic}")
private String requestReplyTopic;
@ -211,16 +213,16 @@ public class EsiaAuthService {
if (tokenResponse.getError() != null) {
throw new RuntimeException(tokenResponse.getError_description());
}
String accessToken = tokenResponse.getAccess_token();
String esiaAccessTokenStr = tokenResponse.getAccess_token();
String esiaRefreshTokenStr = tokenResponse.getRefresh_token();
boolean hasRole = ulDataService.checkRole(accessToken);
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
boolean hasRole = ulDataService.checkRole(esiaAccessTokenStr);
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
String prnOid = esiaAccessToken.getSbj_id();
String refreshToken = tokenResponse.getRefresh_token();
String ervuId = getErvuId(accessToken, prnOid);
String ervuId = getErvuId(esiaAccessTokenStr, prnOid);
Long expiresIn = tokenResponse.getExpires_in();
TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
TokensStore.addRefreshToken(prnOid, refreshToken, expiresIn);
TokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
TokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId, hasRole);
int expiry = tokenResponse.getExpires_in().intValue();
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
@ -228,9 +230,10 @@ public class EsiaAuthService {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
SecurityContext context = SecurityContextHolder.createEmptyContext();
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
JwtAuthentication jwtAuthentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
esiaAccessToken.getSbj_id(), token.getValue());
context.setAuthentication(authentication);
authenticationManager.authenticate(jwtAuthentication);
context.setAuthentication(jwtAuthentication);
SecurityContextHolder.setContext(context);
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
response.addCookie(authMarkerCookie);

View file

@ -0,0 +1,72 @@
package ru.micord.ervu.security.listener;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.DeferredCsrfToken;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
@Component
public class JwtUpdateListener implements ApplicationListener<AuthenticationSuccessEvent> {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final CsrfTokenRepository tokenRepository;
private final Set<AntPathRequestMatcher> csrfUpdateRequiredPathMatchers;
private CsrfTokenRequestHandler requestHandler = new CsrfTokenRequestAttributeHandler();
@Autowired
public JwtUpdateListener(CsrfTokenRepository tokenRepository) {
Assert.notNull(tokenRepository, "tokenRepository cannot be null");
this.tokenRepository = tokenRepository;
this.csrfUpdateRequiredPathMatchers = Arrays.stream(new String[] {"/esia/auth"})
.map(AntPathRequestMatcher::new)
.collect(Collectors.toSet());
}
@Override
public void onApplicationEvent(AuthenticationSuccessEvent event) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes)
.resolveReference(
REFERENCE_REQUEST);
HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
//if csrf cookie update is not required return
if (this.csrfUpdateRequiredPathMatchers.stream()
.noneMatch(matcher -> matcher.matches(request))) {
return;
}
boolean containsToken = this.tokenRepository.loadToken(request) != null;
if (containsToken) {
this.tokenRepository.saveToken(null, request, response);
DeferredCsrfToken deferredCsrfToken = this.tokenRepository.loadDeferredToken(request,
response
);
this.requestHandler.handle(request, response, deferredCsrfToken::get);
this.logger.debug("Replaced CSRF Token");
}
}
}