diff --git a/backend/src/main/java/ru/micord/ervu/security/LogoutSuccessHandler.java b/backend/src/main/java/ru/micord/ervu/security/LogoutSuccessHandler.java index 9993b23..3867b41 100644 --- a/backend/src/main/java/ru/micord/ervu/security/LogoutSuccessHandler.java +++ b/backend/src/main/java/ru/micord/ervu/security/LogoutSuccessHandler.java @@ -25,9 +25,7 @@ public class LogoutSuccessHandler public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { String url = esiaAuthService.logout(request, response); - response.setStatus(HttpServletResponse.SC_OK); - response.getWriter().write(url); - response.getWriter().flush(); + response.sendRedirect(url); CsrfToken csrfToken = this.csrfTokenRepository.generateToken(request); this.csrfTokenRepository.saveToken(csrfToken, request, response); } diff --git a/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java b/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java index 2297c07..a5aaa4d 100644 --- a/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java @@ -24,6 +24,7 @@ import ru.micord.ervu.security.webbpm.jwt.JwtMatcher; import ru.micord.ervu.security.webbpm.jwt.UnauthorizedEntryPoint; 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.service.JwtTokenService; import static ru.micord.ervu.security.SecurityConstants.ESIA_LOGOUT; @@ -97,9 +98,10 @@ public class SecurityConfig { @Bean public JwtAuthenticationFilter jwtAuthenticationFilter(SecurityHelper securityHelper, - AuthenticationManager manager) { + AuthenticationManager manager, + JwtTokenService jwtTokenService) { JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter( - new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper); + new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper, jwtTokenService); jwtAuthenticationFilter.setAuthenticationManager(manager); return jwtAuthenticationFilter; } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java index 6a91c9a..302eef4 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java @@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContext; import ru.micord.ervu.kafka.model.Document; @@ -43,7 +44,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 @@ -201,17 +201,13 @@ public class EsiaAuthService { } String accessToken = tokenResponse.getAccess_token(); String refreshToken = tokenResponse.getRefresh_token(); - byte[] decodedBytes = Base64.getDecoder() - .decode( - accessToken.substring(accessToken.indexOf('.') + 1, accessToken.lastIndexOf('.'))); - String decodedString = new String(decodedBytes); - EsiaAccessToken esiaAccessToken = objectMapper.readValue(decodedString, EsiaAccessToken.class); + EsiaAccessToken esiaAccessToken = personalDataService.readToken(accessToken); String prnOid = esiaAccessToken.getSbj_id(); Long expiresIn = tokenResponse.getExpires_in(); TokensStore.addAccessToken(prnOid, accessToken, expiresIn); TokensStore.addRefreshToken(prnOid, refreshToken, expiresIn); - String ervuId = getErvuId(accessToken); - Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId); + Response ervuIdResponse = getErvuIdResponse(accessToken); + Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId()); int expiry = tokenResponse.getExpires_in().intValue(); Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry); response.addCookie(accessCookie); @@ -224,6 +220,12 @@ public class EsiaAuthService { SecurityContextHolder.setContext(context); Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry); response.addCookie(authMarkerCookie); + if (ervuIdResponse.getErrorData() != null) { + return new ResponseEntity<>( + "Доступ запрещен. " + ervuIdResponse.getErrorData().getName(), + HttpStatus.FORBIDDEN + ); + } return ResponseEntity.ok("Authentication successful"); } catch (Exception e) { @@ -279,18 +281,13 @@ public class EsiaAuthService { } String accessToken = tokenResponse.getAccess_token(); String newRefreshToken = tokenResponse.getRefresh_token(); - - byte[] decodedBytes = Base64.getDecoder() - .decode( - accessToken.substring(accessToken.indexOf('.') + 1, accessToken.lastIndexOf('.'))); - String decodedString = new String(decodedBytes); - EsiaAccessToken esiaAccessToken = objectMapper.readValue(decodedString, EsiaAccessToken.class); + EsiaAccessToken esiaAccessToken = personalDataService.readToken(accessToken); String prnOid = esiaAccessToken.getSbj_id(); Long expiresIn = tokenResponse.getExpires_in(); TokensStore.addAccessToken(prnOid, accessToken, expiresIn); TokensStore.addRefreshToken(prnOid, newRefreshToken, expiresIn); - String ervuId = getErvuId(accessToken); - Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId); + Response ervuIdResponse = getErvuIdResponse(accessToken); + Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId()); int expiry = tokenResponse.getExpires_in().intValue(); Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry); response.addCookie(accessCookie); @@ -360,22 +357,14 @@ public class EsiaAuthService { } } - public String getErvuId(String accessToken) { + public Response getErvuIdResponse(String accessToken) { try { PersonModel personModel = personalDataService.getPersonModel(accessToken); Person person = copyToPerson(personModel); String kafkaResponse = replyingKafkaService.sendMessageAndGetReply(requestTopic, requestReplyTopic, objectMapper.writeValueAsString(person) ); - Response response = objectMapper.readValue(kafkaResponse, Response.class); - if (response.getErrorData() != null) { - throw new RuntimeException( - "Error code = " + response.getErrorData().getCode() + ", error name = " - + response.getErrorData().getName()); - } - else { - return response.getErvuId(); - } + return objectMapper.readValue(kafkaResponse, Response.class); } catch (Exception e) { throw new RuntimeException(e); diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaPersonalDataService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaPersonalDataService.java index 79b11ed..f4590c3 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaPersonalDataService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaPersonalDataService.java @@ -31,11 +31,7 @@ public class EsiaPersonalDataService implements PersonalDataService { @Override public PersonModel getPersonModel(String accessToken) { try { - byte[] decodedBytes = Base64.getDecoder() - .decode( - accessToken.substring(accessToken.indexOf('.') + 1, accessToken.lastIndexOf('.'))); - String decodedString = new String(decodedBytes); - EsiaAccessToken esiaAccessToken = objectMapper.readValue(decodedString, EsiaAccessToken.class); + EsiaAccessToken esiaAccessToken = readToken(accessToken); String prnsId = esiaAccessToken.getSbj_id(); PersonModel personModel = getPersonData(prnsId, accessToken); personModel.setPassportModel( @@ -88,4 +84,23 @@ public class EsiaPersonalDataService implements PersonalDataService { throw new RuntimeException(e); } } + + @Override + public EsiaAccessToken readToken(String accessToken) { + try { + byte[] decodedBytes = Base64.getDecoder() + .decode( + accessToken.substring(accessToken.indexOf('.') + 1, accessToken.lastIndexOf('.')) + .replace('-', '+') + .replace('_', '/')); + String decodedString = new String(decodedBytes); + EsiaAccessToken esiaAccessToken = objectMapper.readValue(decodedString, + EsiaAccessToken.class + ); + return esiaAccessToken; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/PersonalDataService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/PersonalDataService.java index 37edafa..303c9fa 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/PersonalDataService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/PersonalDataService.java @@ -1,5 +1,6 @@ package ru.micord.ervu.security.esia.service; +import ru.micord.ervu.security.esia.model.EsiaAccessToken; import ru.micord.ervu.security.esia.model.PersonModel; /** @@ -8,4 +9,5 @@ import ru.micord.ervu.security.esia.model.PersonModel; public interface PersonalDataService { PersonModel getPersonModel(String accessToken); + EsiaAccessToken readToken(String accessToken); } diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java index 17d74fc..c4f60f7 100644 --- a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java @@ -18,6 +18,8 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro import org.springframework.security.web.util.matcher.RequestMatcher; import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication; 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.service.JwtTokenService; import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.extractAuthToken; @@ -33,25 +35,36 @@ public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFil private final SecurityHelper securityHelper; + private final JwtTokenService jwtTokenService; + public JwtAuthenticationFilter(RequestMatcher requestMatcher, AuthenticationEntryPoint entryPoint, - SecurityHelper securityHelper) { + SecurityHelper securityHelper, + JwtTokenService jwtTokenService) { super(requestMatcher); this.entryPoint = entryPoint; this.securityHelper = securityHelper; + this.jwtTokenService = jwtTokenService; } @Override public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException { - String token = extractAuthToken(httpServletRequest); + String tokenStr = extractAuthToken(httpServletRequest); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { - authentication = new JwtAuthentication(null, null, token); + authentication = new JwtAuthentication(null, null, tokenStr); } try { 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) { securityHelper.clearAccessCookies(httpServletResponse); diff --git a/frontend/src/resources/template/app/component/log_out.html b/frontend/src/resources/template/app/component/log_out.html index ff99c6d..f488796 100644 --- a/frontend/src/resources/template/app/component/log_out.html +++ b/frontend/src/resources/template/app/component/log_out.html @@ -2,4 +2,5 @@
\ No newline at end of file + + \ No newline at end of file diff --git a/frontend/src/ts/modules/app/component/logout.component.ts b/frontend/src/ts/modules/app/component/logout.component.ts index 2b2f765..037c2ce 100644 --- a/frontend/src/ts/modules/app/component/logout.component.ts +++ b/frontend/src/ts/modules/app/component/logout.component.ts @@ -28,9 +28,7 @@ export class LogOutComponent implements OnInit{ } public logout(): void { - this.httpClient.get