Merge branch 'hotfix/1.8.2'
This commit is contained in:
commit
496ea11cef
40 changed files with 730 additions and 258 deletions
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<version>1.8.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
<artifactId>backend</artifactId>
|
||||
|
|
|
|||
|
|
@ -34,20 +34,17 @@ public class EmployeeInfoFileUploadController {
|
|||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("access_token")) {
|
||||
accessToken = cookie.getValue();
|
||||
}
|
||||
else if (cookie.getName().equals("auth_token")) {
|
||||
if (cookie.getName().equals("auth_token")) {
|
||||
authToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (accessToken != null) {
|
||||
if (authToken != null) {
|
||||
String offset = ZonedDateTime.now(TimeZone.getTimeZone(clientTimeZone).toZoneId())
|
||||
.getOffset().getId();
|
||||
|
||||
if (this.fileUploadService.saveEmployeeInformationFile(multipartFile, formType, accessToken,
|
||||
if (this.fileUploadService.saveEmployeeInformationFile(multipartFile, formType,
|
||||
authToken, offset)) {
|
||||
return ResponseEntity.ok("File successfully uploaded.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import org.springframework.kafka.annotation.KafkaListener;
|
|||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.micord.ervu.security.esia.token.TokensStore;
|
||||
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
||||
import ru.micord.ervu.security.esia.model.PersonModel;
|
||||
import ru.micord.ervu.security.esia.service.UlDataService;
|
||||
|
|
@ -72,8 +73,7 @@ public class EmployeeInfoFileUploadService {
|
|||
this.jwtTokenService = jwtTokenService;
|
||||
}
|
||||
|
||||
public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType,
|
||||
String accessToken, String authToken, String offset) {
|
||||
public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType, String authToken, String offset) {
|
||||
String fileUploadUrl = this.url + "/" + getNewFilename(multipartFile.getOriginalFilename());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
|
|
@ -85,10 +85,13 @@ public class EmployeeInfoFileUploadService {
|
|||
String fileId = UUID.randomUUID().toString();
|
||||
String fileName = multipartFile.getOriginalFilename();
|
||||
EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf(formType);
|
||||
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
|
||||
PersonModel personModel = employeeModel.getPerson();
|
||||
Token token = jwtTokenService.getToken(authToken);
|
||||
String[] ids = token.getUserAccountId().split(":");
|
||||
String userId = ids[0];
|
||||
String ervuId = ids[1];
|
||||
String accessToken = TokensStore.getAccessToken(userId);
|
||||
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
|
||||
PersonModel personModel = employeeModel.getPerson();
|
||||
String departureDateTime = now.format(DateTimeFormatter.ofPattern(FORMAT));
|
||||
String jsonMessage = getJsonKafkaMessage(
|
||||
employeeInfoKafkaMessageService.getKafkaMessage(
|
||||
|
|
@ -100,15 +103,15 @@ public class EmployeeInfoFileUploadService {
|
|||
accessToken,
|
||||
offset,
|
||||
fileStatus,
|
||||
ids[1],
|
||||
ids[0],
|
||||
ervuId,
|
||||
userId,
|
||||
personModel
|
||||
)
|
||||
);
|
||||
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
|
||||
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
|
||||
convertToFio(personModel.getFirstName(), personModel.getMiddleName(), personModel.getLastName()),
|
||||
(int) multipartFile.getSize(), ids[1]);
|
||||
(int) multipartFile.getSize(), ervuId);
|
||||
return sendMessage(jsonMessage);
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package ru.micord.ervu.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
||||
import ru.micord.ervu.security.esia.service.EsiaAuthService;
|
||||
|
||||
public class LogoutSuccessHandler
|
||||
implements org.springframework.security.web.authentication.logout.LogoutSuccessHandler {
|
||||
|
||||
private final CsrfTokenRepository csrfTokenRepository;
|
||||
private final EsiaAuthService esiaAuthService;
|
||||
|
||||
public LogoutSuccessHandler(CsrfTokenRepository csrfTokenRepository,
|
||||
EsiaAuthService esiaAuthService) {
|
||||
this.csrfTokenRepository = csrfTokenRepository;
|
||||
this.esiaAuthService = esiaAuthService;
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
CsrfToken csrfToken = this.csrfTokenRepository.generateToken(request);
|
||||
this.csrfTokenRepository.saveToken(csrfToken, request, response);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,59 +4,105 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import ru.micord.ervu.security.webbpm.jwt.filter.JwtAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
||||
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
|
||||
import org.springframework.web.filter.RequestContextFilter;
|
||||
import ru.micord.ervu.security.esia.service.EsiaAuthService;
|
||||
import ru.micord.ervu.security.filter.FilterChainExceptionHandler;
|
||||
import ru.micord.ervu.security.webbpm.jwt.JwtAuthenticationProvider;
|
||||
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;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public class SecurityConfig {
|
||||
private static final String[] PERMIT_ALL = new String[] {
|
||||
"/version", "/esia/url", "/esia/auth", "esia/refresh"
|
||||
};
|
||||
@Autowired
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
@Autowired
|
||||
private EsiaAuthService esiaAuthService;
|
||||
@Autowired
|
||||
private FilterChainExceptionHandler filterChainExceptionHandler;
|
||||
@Autowired
|
||||
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
||||
|
||||
@Autowired
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
auth.authenticationProvider(jwtAuthenticationProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
@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();
|
||||
}
|
||||
|
||||
httpConfigure(http);
|
||||
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
protected void httpConfigure(HttpSecurity httpSecurity) throws Exception {
|
||||
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||
tokenRepository.setCookiePath("/");
|
||||
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(
|
||||
(authorizeHttpRequests) -> authorizeHttpRequests.requestMatchers(PERMIT_ALL)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.csrf((csrf) -> csrf.csrfTokenRepository(tokenRepository)
|
||||
.csrfTokenRequestHandler(requestHandler))
|
||||
.logout((logout) -> logout.logoutUrl(ESIA_LOGOUT)
|
||||
.logoutSuccessHandler(new LogoutSuccessHandler(tokenRepository, esiaAuthService)))
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(entryPoint())
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
}
|
||||
|
||||
protected void httpConfigure(HttpSecurity httpSecurity) throws Exception {
|
||||
String[] permitAll = {"/version", "/esia/url", "/esia/auth", "esia/refresh", "/esia/logout"};
|
||||
public AuthenticationEntryPoint entryPoint() {
|
||||
return new UnauthorizedEntryPoint();
|
||||
}
|
||||
|
||||
httpSecurity.authorizeRequests()
|
||||
.antMatchers(permitAll).permitAll()
|
||||
.antMatchers("/**").authenticated()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.exceptionHandling().authenticationEntryPoint(entryPoint())
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
}
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(
|
||||
AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
||||
return authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
public AuthenticationEntryPoint entryPoint() {
|
||||
return new UnauthorizedEntryPoint();
|
||||
}
|
||||
@Bean
|
||||
public SecurityHelper securityHelper() {
|
||||
return new SecurityHelper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
|
||||
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter("/**",
|
||||
entryPoint()
|
||||
);
|
||||
jwtAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
|
||||
return jwtAuthenticationFilter;
|
||||
}
|
||||
@Bean
|
||||
public JwtAuthenticationFilter jwtAuthenticationFilter(SecurityHelper securityHelper,
|
||||
AuthenticationManager manager,
|
||||
JwtTokenService jwtTokenService) {
|
||||
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(
|
||||
new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper, jwtTokenService);
|
||||
jwtAuthenticationFilter.setAuthenticationManager(manager);
|
||||
return jwtAuthenticationFilter;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package ru.micord.ervu.security;
|
||||
|
||||
public class SecurityConstants {
|
||||
public static final String ESIA_LOGOUT = "/esia/logout";
|
||||
}
|
||||
|
|
@ -3,13 +3,14 @@ package ru.micord.ervu.security.esia.controller;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import ru.micord.ervu.security.SecurityConstants;
|
||||
import ru.micord.ervu.security.esia.model.OrgInfoModel;
|
||||
import ru.micord.ervu.security.esia.service.EsiaAuthService;
|
||||
import ru.micord.ervu.security.esia.service.EsiaDataService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
|
@ -25,38 +26,33 @@ public class EsiaController {
|
|||
@Autowired
|
||||
private EsiaDataService esiaDataService;
|
||||
|
||||
@RequestMapping(value = "/esia/url")
|
||||
@GetMapping(value = "/esia/url")
|
||||
public String getEsiaUrl() {
|
||||
return esiaAuthService.generateAuthCodeUrl();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/esia/auth", params = "code", method = RequestMethod.GET)
|
||||
@GetMapping(value = "/esia/auth", params = "code")
|
||||
public ResponseEntity<?> esiaAuth(@RequestParam("code") String code, HttpServletRequest request, HttpServletResponse response) {
|
||||
return esiaAuthService.getEsiaTokensByCode(code, request, response);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/esia/refresh")
|
||||
@PostMapping(value = "/esia/refresh")
|
||||
public void refreshToken(HttpServletRequest request, HttpServletResponse response) {
|
||||
esiaAuthService.getEsiaTokensByRefreshToken(request, response);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/esia/org")
|
||||
@GetMapping(value = "/esia/org")
|
||||
public OrgInfoModel getOrgInfo(HttpServletRequest request) {
|
||||
return esiaDataService.getOrgInfo(request);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/esia/userfullname")
|
||||
@GetMapping(value = "/esia/userfullname")
|
||||
public String getUserFullname(HttpServletRequest request) {
|
||||
return esiaDataService.getUserFullname(request);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/esia/orgunitname")
|
||||
@GetMapping(value = "/esia/orgunitname")
|
||||
public String getOrgUnitName(HttpServletRequest request) {
|
||||
return esiaDataService.getOrgUnitName(request);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/esia/logout")
|
||||
public String logout(HttpServletRequest request, HttpServletResponse response) {
|
||||
return esiaAuthService.logout(request, response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ervu.service.okopf.OkopfService;
|
||||
import ru.micord.ervu.security.esia.token.TokensStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
|
@ -46,37 +47,31 @@ import ru.micord.ervu.security.esia.model.EsiaTokenResponse;
|
|||
import ru.micord.ervu.security.esia.model.FormUrlencoded;
|
||||
import ru.micord.ervu.security.esia.model.OrganizationModel;
|
||||
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
|
||||
*/
|
||||
@Service
|
||||
public class EsiaAuthService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Value("${cookie-path:#{null}}")
|
||||
private String path;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private EsiaConfig esiaConfig;
|
||||
|
||||
@Autowired
|
||||
private UlDataService ulDataService;
|
||||
|
||||
@Autowired
|
||||
private JwtTokenService jwtTokenService;
|
||||
|
||||
@Autowired
|
||||
private ReplyingKafkaService replyingKafkaService;
|
||||
|
||||
@Autowired
|
||||
private OkopfService okopfService;
|
||||
@Autowired
|
||||
private SecurityHelper securityHelper;
|
||||
|
||||
@Value("${ervu.kafka.org.reply.topic}")
|
||||
private String requestReplyTopic;
|
||||
|
|
@ -205,45 +200,31 @@ public class EsiaAuthService {
|
|||
.build()
|
||||
.send(postReq, HttpResponse.BodyHandlers.ofString());
|
||||
String responseString = postResp.body();
|
||||
EsiaTokenResponse tokenResponse = objectMapper.readValue(responseString, EsiaTokenResponse.class);
|
||||
if (tokenResponse != null && tokenResponse.getError() != null) {
|
||||
EsiaTokenResponse tokenResponse = objectMapper.readValue(responseString,
|
||||
EsiaTokenResponse.class
|
||||
);
|
||||
|
||||
if (tokenResponse == null) {
|
||||
throw new IllegalStateException("Got empty esia response");
|
||||
}
|
||||
|
||||
if (tokenResponse.getError() != null) {
|
||||
throw new RuntimeException(tokenResponse.getError_description());
|
||||
}
|
||||
String accessToken = tokenResponse.getAccess_token();
|
||||
|
||||
boolean hasRole = ulDataService.checkRole(accessToken);
|
||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
|
||||
String prnOid = esiaAccessToken.getSbj_id();
|
||||
if (!hasRole) {
|
||||
LOGGER.error("The user with id = " + prnOid + " does not have the required role");
|
||||
return new ResponseEntity<>(
|
||||
"Доступ запрещен. Пользователь должен быть включен в группу \"Сотрудник, ответственный за военно-учетную работу\" в ЕСИА",
|
||||
HttpStatus.FORBIDDEN
|
||||
);
|
||||
}
|
||||
String cookiePath = null;
|
||||
if (path != null) {
|
||||
cookiePath = path;
|
||||
}
|
||||
else {
|
||||
cookiePath = request.getContextPath();
|
||||
}
|
||||
Cookie cookie = new Cookie("access_token", accessToken);
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setPath(cookiePath);
|
||||
response.addCookie(cookie);
|
||||
|
||||
String refreshToken = tokenResponse.getRefresh_token();
|
||||
Cookie cookieRefresh = new Cookie("refresh_token", refreshToken);
|
||||
cookieRefresh.setHttpOnly(true);
|
||||
cookieRefresh.setPath(cookiePath);
|
||||
response.addCookie(cookieRefresh);
|
||||
|
||||
String ervuId = getErvuId(accessToken, esiaAccessToken.getSbj_id());
|
||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), tokenResponse.getExpires_in(), ervuId);
|
||||
Cookie authToken = new Cookie("auth_token", token.getValue());
|
||||
authToken.setPath(cookiePath);
|
||||
authToken.setHttpOnly(true);
|
||||
response.addCookie(authToken);
|
||||
String ervuId = getErvuId(accessToken, prnOid);
|
||||
Long expiresIn = tokenResponse.getExpires_in();
|
||||
TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
|
||||
TokensStore.addRefreshToken(prnOid, refreshToken, expiresIn);
|
||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId, hasRole);
|
||||
int expiry = tokenResponse.getExpires_in().intValue();
|
||||
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
|
||||
response.addCookie(accessCookie);
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
|
|
@ -251,11 +232,15 @@ public class EsiaAuthService {
|
|||
esiaAccessToken.getSbj_id(), token.getValue());
|
||||
context.setAuthentication(authentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
|
||||
Cookie isAuth = new Cookie("webbpm.ervu-lkrp-ul", "true");
|
||||
isAuth.setMaxAge(tokenResponse.getExpires_in().intValue());
|
||||
isAuth.setPath("/");
|
||||
response.addCookie(isAuth);
|
||||
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
|
||||
response.addCookie(authMarkerCookie);
|
||||
if (!hasRole) {
|
||||
LOGGER.error("The user with id = " + prnOid + " does not have the required role");
|
||||
return new ResponseEntity<>(
|
||||
"Доступ запрещен. Пользователь должен быть включен в группу \"Сотрудник, ответственный за военно-учетную работу\" в ЕСИА",
|
||||
HttpStatus.FORBIDDEN
|
||||
);
|
||||
}
|
||||
return ResponseEntity.ok("Authentication successful");
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
|
@ -265,15 +250,7 @@ public class EsiaAuthService {
|
|||
|
||||
public void getEsiaTokensByRefreshToken(HttpServletRequest request, HttpServletResponse response) {
|
||||
try {
|
||||
String refreshToken = null;
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("refresh_token")) {
|
||||
refreshToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
String refreshToken = jwtTokenService.getRefreshToken(request);
|
||||
String clientId = esiaConfig.getClientId();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
||||
ZonedDateTime dt = ZonedDateTime.now();
|
||||
|
|
@ -322,30 +299,17 @@ public class EsiaAuthService {
|
|||
throw new RuntimeException(tokenResponse.getError_description());
|
||||
}
|
||||
String accessToken = tokenResponse.getAccess_token();
|
||||
Cookie cookie = new Cookie("access_token", accessToken);
|
||||
cookie.setHttpOnly(true);
|
||||
String cookiePath = null;
|
||||
if (path != null) {
|
||||
cookiePath = path;
|
||||
}
|
||||
else {
|
||||
cookiePath = request.getContextPath();
|
||||
}
|
||||
cookie.setPath(cookiePath);
|
||||
response.addCookie(cookie);
|
||||
|
||||
String newRefreshToken = tokenResponse.getRefresh_token();
|
||||
Cookie cookieRefresh = new Cookie("refresh_token", newRefreshToken);
|
||||
cookieRefresh.setHttpOnly(true);
|
||||
cookieRefresh.setPath(cookiePath);
|
||||
response.addCookie(cookieRefresh);
|
||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
|
||||
String ervuId = getErvuId(accessToken, esiaAccessToken.getSbj_id());
|
||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), tokenResponse.getExpires_in(), ervuId);
|
||||
Cookie authToken = new Cookie("auth_token", token.getValue());
|
||||
authToken.setPath(cookiePath);
|
||||
authToken.setHttpOnly(true);
|
||||
response.addCookie(authToken);
|
||||
String prnOid = esiaAccessToken.getSbj_id();
|
||||
Long expiresIn = tokenResponse.getExpires_in();
|
||||
TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
|
||||
TokensStore.addRefreshToken(prnOid, newRefreshToken, expiresIn);
|
||||
String ervuId = getErvuId(accessToken, prnOid);
|
||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId, true);
|
||||
int expiry = tokenResponse.getExpires_in().intValue();
|
||||
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
|
||||
response.addCookie(accessCookie);
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
|
|
@ -353,11 +317,8 @@ public class EsiaAuthService {
|
|||
esiaAccessToken.getSbj_id(), token.getValue());
|
||||
context.setAuthentication(authentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
|
||||
Cookie isAuth = new Cookie("webbpm.ervu-lkrp-ul", "true");
|
||||
isAuth.setMaxAge(tokenResponse.getExpires_in().intValue());
|
||||
isAuth.setPath("/");
|
||||
response.addCookie(isAuth);
|
||||
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
|
||||
response.addCookie(authMarkerCookie);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
|
@ -398,23 +359,10 @@ public class EsiaAuthService {
|
|||
|
||||
public String logout(HttpServletRequest request, HttpServletResponse response) {
|
||||
try {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null)
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("webbpm.ervu-lkrp-ul")) {
|
||||
cookie.setValue("");
|
||||
cookie.setPath("/");
|
||||
cookie.setMaxAge(0);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
else if (cookie.getName().equals("auth_token") || cookie.getName().equals("refresh_token")
|
||||
|| cookie.getName().equals("access_token")) {
|
||||
cookie.setValue("");
|
||||
cookie.setPath(cookie.getPath());
|
||||
cookie.setMaxAge(0);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
}
|
||||
securityHelper.clearAccessCookies(response);
|
||||
String userId = jwtTokenService.getUserAccountId(request);
|
||||
TokensStore.removeAccessToken(userId);
|
||||
TokensStore.removeRefreshToken(userId);
|
||||
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
|
||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||
URL url = new URL(logoutUrl);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
package ru.micord.ervu.security.esia.service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.security.esia.model.*;
|
||||
import ru.micord.ervu.security.esia.model.Addresses;
|
||||
import ru.micord.ervu.security.esia.model.Contacts;
|
||||
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
||||
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
||||
import ru.micord.ervu.security.esia.model.OrgInfoModel;
|
||||
import ru.micord.ervu.security.esia.model.OrganizationModel;
|
||||
import ru.micord.ervu.security.esia.model.PersonModel;
|
||||
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
|
|
@ -17,8 +23,11 @@ public class EsiaDataService {
|
|||
@Autowired
|
||||
private UlDataService ulDataService;
|
||||
|
||||
@Autowired
|
||||
private JwtTokenService jwtTokenService;
|
||||
|
||||
public OrgInfoModel getOrgInfo(HttpServletRequest request) {
|
||||
String accessToken = getAccessToken(request);
|
||||
String accessToken = jwtTokenService.getAccessToken(request);
|
||||
if (accessToken == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -69,7 +78,7 @@ public class EsiaDataService {
|
|||
}
|
||||
|
||||
public String getOrgUnitName(HttpServletRequest request) {
|
||||
String accessToken = getAccessToken(request);
|
||||
String accessToken = jwtTokenService.getAccessToken(request);
|
||||
if (accessToken == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -78,7 +87,7 @@ public class EsiaDataService {
|
|||
}
|
||||
|
||||
public String getUserFullname(HttpServletRequest request) {
|
||||
String accessToken = getAccessToken(request);
|
||||
String accessToken = jwtTokenService.getAccessToken(request);
|
||||
if (accessToken == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -86,16 +95,4 @@ public class EsiaDataService {
|
|||
PersonModel personModel = ulDataService.getPersonData(esiaAccessToken.getSbj_id(), accessToken);
|
||||
return personModel.getLastName() + " " + personModel.getFirstName().charAt(0) + ". " + personModel.getMiddleName().charAt(0) + ".";
|
||||
}
|
||||
|
||||
private String getAccessToken(HttpServletRequest request) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("access_token")) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package ru.micord.ervu.security.esia.token;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
public class ExpiringToken {
|
||||
private String accessToken;
|
||||
private long expiryTime;
|
||||
|
||||
public ExpiringToken(String accessToken, long expiryTime) {
|
||||
this.accessToken = accessToken;
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public long getExpiryTime() {
|
||||
return expiryTime;
|
||||
}
|
||||
|
||||
public void setExpiryTime(long expiryTime) {
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
boolean isExpired() {
|
||||
return System.currentTimeMillis() > expiryTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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() {
|
||||
TokensStore.removeExpiredRefreshToken();
|
||||
TokensStore.removeExpiredAccessToken();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package ru.micord.ervu.security.esia.token;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
public class TokensStore {
|
||||
private static final Map<String, ExpiringToken> accessTokensMap = new ConcurrentHashMap<>();
|
||||
private static final Map<String, ExpiringToken> refreshTokensMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static void addAccessToken(String prnOid, String token, long expiresIn) {
|
||||
if (token != null) {
|
||||
long expiryTime = System.currentTimeMillis() + 1000L * expiresIn;
|
||||
accessTokensMap.put(prnOid, new ExpiringToken(token, expiryTime));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAccessToken(String prnOid) {
|
||||
return accessTokensMap.get(prnOid).getAccessToken();
|
||||
}
|
||||
|
||||
public static void removeExpiredAccessToken() {
|
||||
for (String key : accessTokensMap.keySet()) {
|
||||
ExpiringToken token = accessTokensMap.get(key);
|
||||
if (token != null && token.isExpired()) {
|
||||
accessTokensMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeExpiredRefreshToken() {
|
||||
for (String key : refreshTokensMap.keySet()) {
|
||||
ExpiringToken token = refreshTokensMap.get(key);
|
||||
if (token != null && token.isExpired()) {
|
||||
refreshTokensMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeAccessToken(String prnOid) {
|
||||
accessTokensMap.remove(prnOid);
|
||||
}
|
||||
|
||||
public static void addRefreshToken(String prnOid, String token, long expiresIn) {
|
||||
if (token != null) {
|
||||
long expiryTime = System.currentTimeMillis() + 1000L * expiresIn;
|
||||
refreshTokensMap.put(prnOid, new ExpiringToken(token, expiryTime));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getRefreshToken(String prnOid) {
|
||||
return refreshTokensMap.get(prnOid).getAccessToken();
|
||||
}
|
||||
|
||||
public static void removeRefreshToken(String prnOid) {
|
||||
refreshTokensMap.remove(prnOid);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package ru.micord.ervu.security.filter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
|
||||
@Component
|
||||
public class FilterChainExceptionHandler extends OncePerRequestFilter {
|
||||
@Autowired
|
||||
@Qualifier("handlerExceptionResolver")
|
||||
private HandlerExceptionResolver resolver;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) {
|
||||
try {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
catch (Exception e) {
|
||||
resolver.resolveException(request, response, null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package ru.micord.ervu.security.webbpm.jwt;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
|
|
@ -44,10 +46,12 @@ public class JwtAuthenticationProvider implements AuthenticationProvider {
|
|||
throw new BadCredentialsException("Auth token is not valid for user " + token.getUserAccountId());
|
||||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
|
||||
UsernamePasswordAuthenticationToken pwdToken =
|
||||
UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
return new JwtAuthentication(usernamePasswordAuthenticationToken, token.getUserAccountId(), token.getValue());
|
||||
return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package ru.micord.ervu.security.webbpm.jwt;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.extractAuthToken;
|
||||
|
||||
public final class JwtMatcher implements RequestMatcher {
|
||||
private final Set<AntPathRequestMatcher> excludedPathMatchers;
|
||||
private final AntPathRequestMatcher securedMatcher;
|
||||
|
||||
public JwtMatcher(String securedPath, String... excludedPaths) {
|
||||
this.securedMatcher = new AntPathRequestMatcher(securedPath);
|
||||
this.excludedPathMatchers = Arrays.stream(excludedPaths)
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
if (this.excludedPathMatchers.stream().anyMatch(matcher -> matcher.matches(request))) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return extractAuthToken(request) != null && this.securedMatcher.matches(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,10 @@ import java.io.IOException;
|
|||
import java.lang.invoke.MethodHandles;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
|
|
@ -16,70 +16,80 @@ import org.springframework.security.core.AuthenticationException;
|
|||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* @author Flyur Karimov
|
||||
*/
|
||||
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(
|
||||
MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final AuthenticationEntryPoint entryPoint;
|
||||
|
||||
public JwtAuthenticationFilter(String securityPath, AuthenticationEntryPoint entryPoint) {
|
||||
super(securityPath);
|
||||
private final SecurityHelper securityHelper;
|
||||
|
||||
private final JwtTokenService jwtTokenService;
|
||||
|
||||
public JwtAuthenticationFilter(RequestMatcher requestMatcher,
|
||||
AuthenticationEntryPoint entryPoint,
|
||||
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 = extractAuthTokenFromRequest(httpServletRequest);
|
||||
HttpServletResponse httpServletResponse)
|
||||
throws AuthenticationException {
|
||||
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);
|
||||
if (!token.getHasRole()) {
|
||||
throw new CredentialsExpiredException("Invalid token. User has no required role");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CredentialsExpiredException e) {
|
||||
securityHelper.clearAccessCookies(httpServletResponse);
|
||||
httpServletResponse.setStatus(401);
|
||||
LOGGER.warn(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
|
||||
return extractAuthTokenFromRequest(request) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain chain, Authentication authentication) throws IOException, ServletException {
|
||||
FilterChain chain, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException, ServletException {
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException exception)
|
||||
throws IOException, ServletException {
|
||||
LOGGER.error("Jwt unsuccessful authentication exception", exception);
|
||||
SecurityContextHolder.clearContext();
|
||||
entryPoint.commence(request, response, exception);
|
||||
}
|
||||
|
||||
public String extractAuthTokenFromRequest(HttpServletRequest httpRequest) {
|
||||
String token = null;
|
||||
Cookie[] cookies = httpRequest.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("auth_token")) {
|
||||
token = cookie.getValue();
|
||||
LOGGER.info("Token extracted from cookie: {}", token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package ru.micord.ervu.security.webbpm.jwt.helper;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
|
||||
|
||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.AUTH_MARKER;
|
||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.AUTH_TOKEN;
|
||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.createCookie;
|
||||
|
||||
public final class SecurityHelper {
|
||||
@Value("${cookie.path:#{null}}")
|
||||
private String accessCookiePath;
|
||||
|
||||
public void clearAccessCookies(HttpServletResponse response) {
|
||||
Cookie tokenCookie = createCookie(AUTH_TOKEN, null, null);
|
||||
tokenCookie.setMaxAge(0);
|
||||
tokenCookie.setPath(accessCookiePath);
|
||||
tokenCookie.setHttpOnly(true);
|
||||
response.addCookie(tokenCookie);
|
||||
|
||||
Cookie markerCookie = createCookie(AUTH_MARKER, null, null);
|
||||
markerCookie.setMaxAge(0);
|
||||
markerCookie.setPath("/");
|
||||
response.addCookie(markerCookie);
|
||||
}
|
||||
|
||||
public Cookie createAccessCookie(String cookieValue, int expiry) {
|
||||
Cookie authToken = createCookie(SecurityUtil.AUTH_TOKEN, cookieValue, accessCookiePath);
|
||||
authToken.setPath(accessCookiePath);
|
||||
authToken.setMaxAge(expiry);
|
||||
return authToken;
|
||||
}
|
||||
|
||||
public Cookie createAuthMarkerCookie(String cookieValue, int expiry) {
|
||||
Cookie marker = createCookie(AUTH_MARKER, cookieValue, "/");
|
||||
marker.setMaxAge(expiry);
|
||||
marker.setHttpOnly(false);
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,12 +7,14 @@ public class Token {
|
|||
private final String issuer;
|
||||
private final Date expirationDate;
|
||||
private final String value;
|
||||
private final Boolean hasRole;
|
||||
|
||||
public Token(String userAccountId, String issuer, Date expirationDate, String value) {
|
||||
public Token(String userAccountId, String issuer, Date expirationDate, String value, Boolean hasRole) {
|
||||
this.userAccountId = userAccountId;
|
||||
this.issuer = issuer;
|
||||
this.expirationDate = expirationDate;
|
||||
this.value = value;
|
||||
this.hasRole = hasRole;
|
||||
}
|
||||
|
||||
public String getUserAccountId() {
|
||||
|
|
@ -34,4 +36,8 @@ public class Token {
|
|||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Boolean getHasRole() {
|
||||
return hasRole;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import java.lang.invoke.MethodHandles;
|
|||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
|
|
@ -13,10 +14,13 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.micord.ervu.security.esia.token.TokensStore;
|
||||
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||
|
||||
import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils;
|
||||
|
||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.extractAuthToken;
|
||||
|
||||
/**
|
||||
* @author Flyur Karimov
|
||||
*/
|
||||
|
|
@ -38,7 +42,7 @@ public class JwtTokenService {
|
|||
this.SIGNING_KEY = Keys.hmacShaKeyFor(encodedKey);
|
||||
}
|
||||
|
||||
public Token createAccessToken(String userAccountId, Long expiresIn, String ervuId) {
|
||||
public Token createAccessToken(String userAccountId, Long expiresIn, String ervuId, Boolean hasRole) {
|
||||
|
||||
Date expirationDate = new Date(System.currentTimeMillis() + 1000L * expiresIn);
|
||||
String value = Jwts.builder()
|
||||
|
|
@ -46,9 +50,10 @@ public class JwtTokenService {
|
|||
.setIssuer(tokenIssuerName)
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(expirationDate)
|
||||
.claim("hasRole", hasRole)
|
||||
.signWith(SIGNING_KEY)
|
||||
.compact();
|
||||
return new Token(userAccountId + ":" + ervuId, tokenIssuerName, expirationDate, value);
|
||||
return new Token(userAccountId + ":" + ervuId, tokenIssuerName, expirationDate, value, hasRole);
|
||||
}
|
||||
|
||||
public boolean isValid(Token token) {
|
||||
|
|
@ -70,6 +75,26 @@ public class JwtTokenService {
|
|||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
|
||||
return new Token(claims.getSubject(), claims.getIssuer(), claims.getExpiration(), token);
|
||||
return new Token(claims.getSubject(), claims.getIssuer(), claims.getExpiration(), token, claims.get("hasRole", Boolean.class));
|
||||
}
|
||||
|
||||
public String getAccessToken(HttpServletRequest request) {
|
||||
return TokensStore.getAccessToken(getUserAccountId(request));
|
||||
}
|
||||
|
||||
public String getRefreshToken(HttpServletRequest request) {
|
||||
return TokensStore.getRefreshToken(getUserAccountId(request));
|
||||
}
|
||||
|
||||
public String getUserAccountId(HttpServletRequest request) {
|
||||
String authToken = extractAuthToken(request);
|
||||
|
||||
if (authToken != null) {
|
||||
String[] ids = getToken(authToken).getUserAccountId().split(":");
|
||||
return ids[0];
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Failed to get auth data. User unauthorized.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package ru.micord.ervu.security.webbpm.jwt.util;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
|
||||
|
||||
public final class SecurityUtil {
|
||||
public static final String AUTH_TOKEN = "auth_token";
|
||||
|
||||
public static final String AUTH_MARKER = "webbpm.ervu-lkrp-ul";
|
||||
|
||||
private SecurityUtil() {
|
||||
//empty
|
||||
}
|
||||
|
||||
public static Cookie createCookie(String name, String value, String path) {
|
||||
String cookieValue = value != null ? URLEncoder.encode(value, StandardCharsets.UTF_8) : null;
|
||||
Cookie cookie = new Cookie(name, cookieValue);
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(
|
||||
REFERENCE_REQUEST);
|
||||
|
||||
if (path != null) {
|
||||
cookie.setPath(path);
|
||||
}
|
||||
else {
|
||||
cookie.setPath(request.getContextPath());
|
||||
}
|
||||
cookie.setHttpOnly(true);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
public static String extractAuthToken(HttpServletRequest httpRequest) {
|
||||
Cookie cookie = WebUtils.getCookie(httpRequest, AUTH_TOKEN);
|
||||
return cookie != null ? cookie.getValue() : null;
|
||||
}
|
||||
}
|
||||
|
|
@ -51,3 +51,6 @@ ERVU_FILE_UPLOAD_FILE_SIZE_THRESHOLD=0
|
|||
S3_ENDPOINT=http://ervu-minio.k8s.micord.ru:31900
|
||||
S3_ACCESS_KEY=rlTdTvkmSXu9FsLhfecw
|
||||
S3_SECRET_KEY=NUmY0wwRIEyAd98GCKd1cOgJWvLQYAcMMul5Ulu0
|
||||
|
||||
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
|
||||
COOKIE_PATH=/ul
|
||||
|
|
@ -94,6 +94,7 @@
|
|||
<property name="s3.secret_key" value="NUmY0wwRIEyAd98GCKd1cOgJWvLQYAcMMul5Ulu0"/>
|
||||
<property name="av.kafka.group.id" value="1"/>
|
||||
<property name="av.kafka.download.response" value="ervu.lkrp.av-fileupload-status"/>
|
||||
<property name="esia.token.clear.cron" value="0 0 */1 * * *"/>
|
||||
<property name="esia.upload.data.role" value="MNSV89_UPLOAD_DATA"/>
|
||||
</system-properties>
|
||||
<management>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<version>1.8.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<version>1.8.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
|
|
@ -796,11 +796,11 @@
|
|||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
padding-left: 60px;
|
||||
padding-left: 56px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-name::before {
|
||||
position: absolute;
|
||||
content: url(../img/svg/file-xlsx.svg);
|
||||
content: url(../img/svg/file-csv.svg);
|
||||
top: -2px;
|
||||
left: 0;
|
||||
}
|
||||
|
|
|
|||
11
frontend/src/resources/img/svg/file-csv.svg
Normal file
11
frontend/src/resources/img/svg/file-csv.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<svg width="42" height="24" viewBox="0 0 42 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2036_4892)">
|
||||
<path d="M3.73732 24C0.852775 24 -0.941093 20.6418 0.519071 17.9752L7.65122 4.9504C8.99126 2.50318 11.4404 1 14.0877 1H38.2683C40.3292 1 42 2.79086 42 5V20C42 22.2091 40.3292 24 38.2683 24H3.73732Z" fill="#2DC36A"/>
|
||||
<path d="M19.768 11.336C19.7253 11.3947 19.6827 11.44 19.64 11.472C19.5973 11.504 19.5387 11.52 19.464 11.52C19.384 11.52 19.296 11.488 19.2 11.424C19.104 11.3547 18.984 11.28 18.84 11.2C18.7013 11.12 18.528 11.048 18.32 10.984C18.1173 10.9147 17.8667 10.88 17.568 10.88C17.168 10.88 16.816 10.952 16.512 11.096C16.208 11.2347 15.952 11.4373 15.744 11.704C15.5413 11.9707 15.3867 12.2933 15.28 12.672C15.1787 13.0507 15.128 13.4747 15.128 13.944C15.128 14.4347 15.184 14.872 15.296 15.256C15.408 15.6347 15.5653 15.9547 15.768 16.216C15.976 16.472 16.224 16.6693 16.512 16.808C16.8053 16.9413 17.1333 17.008 17.496 17.008C17.8427 17.008 18.128 16.968 18.352 16.888C18.576 16.8027 18.76 16.7093 18.904 16.608C19.0533 16.5067 19.176 16.416 19.272 16.336C19.3733 16.2507 19.472 16.208 19.568 16.208C19.6853 16.208 19.776 16.2533 19.84 16.344L20.24 16.864C20.064 17.0827 19.864 17.2693 19.64 17.424C19.416 17.5787 19.1733 17.7093 18.912 17.816C18.656 17.9173 18.3867 17.992 18.104 18.04C17.8213 18.088 17.5333 18.112 17.24 18.112C16.7333 18.112 16.2613 18.0187 15.824 17.832C15.392 17.6453 15.016 17.376 14.696 17.024C14.376 16.6667 14.1253 16.2293 13.944 15.712C13.7627 15.1947 13.672 14.6053 13.672 13.944C13.672 13.3413 13.7547 12.784 13.92 12.272C14.0907 11.76 14.336 11.32 14.656 10.952C14.9813 10.5787 15.3787 10.288 15.848 10.08C16.3227 9.872 16.8667 9.768 17.48 9.768C18.0507 9.768 18.552 9.86133 18.984 10.048C19.4213 10.2293 19.808 10.488 20.144 10.824L19.768 11.336ZM26.4886 11.232C26.4246 11.3493 26.326 11.408 26.1926 11.408C26.1126 11.408 26.022 11.3787 25.9206 11.32C25.8193 11.2613 25.694 11.1973 25.5446 11.128C25.4006 11.0533 25.2273 10.9867 25.0246 10.928C24.822 10.864 24.582 10.832 24.3046 10.832C24.0646 10.832 23.8486 10.864 23.6566 10.928C23.4646 10.9867 23.2993 11.0693 23.1606 11.176C23.0273 11.2827 22.9233 11.408 22.8486 11.552C22.7793 11.6907 22.7446 11.8427 22.7446 12.008C22.7446 12.216 22.8033 12.3893 22.9206 12.528C23.0433 12.6667 23.2033 12.7867 23.4006 12.888C23.598 12.9893 23.822 13.08 24.0726 13.16C24.3233 13.2347 24.5793 13.3173 24.8406 13.408C25.1073 13.4933 25.366 13.5893 25.6166 13.696C25.8673 13.8027 26.0913 13.936 26.2886 14.096C26.486 14.256 26.6433 14.4533 26.7606 14.688C26.8833 14.9173 26.9446 15.1947 26.9446 15.52C26.9446 15.8933 26.878 16.24 26.7446 16.56C26.6113 16.8747 26.414 17.1493 26.1526 17.384C25.8913 17.6133 25.5713 17.7947 25.1926 17.928C24.814 18.0613 24.3766 18.128 23.8806 18.128C23.3153 18.128 22.8033 18.0373 22.3446 17.856C21.886 17.6693 21.4966 17.432 21.1766 17.144L21.5126 16.6C21.5553 16.5307 21.606 16.4773 21.6646 16.44C21.7233 16.4027 21.798 16.384 21.8886 16.384C21.9846 16.384 22.086 16.4213 22.1926 16.496C22.2993 16.5707 22.4273 16.6533 22.5766 16.744C22.7313 16.8347 22.918 16.9173 23.1366 16.992C23.3553 17.0667 23.6273 17.104 23.9526 17.104C24.23 17.104 24.4726 17.0693 24.6806 17C24.8886 16.9253 25.062 16.8267 25.2006 16.704C25.3393 16.5813 25.4406 16.44 25.5046 16.28C25.574 16.12 25.6086 15.9493 25.6086 15.768C25.6086 15.544 25.5473 15.36 25.4246 15.216C25.3073 15.0667 25.15 14.9413 24.9526 14.84C24.7553 14.7333 24.5286 14.6427 24.2726 14.568C24.022 14.488 23.7633 14.4053 23.4966 14.32C23.2353 14.2347 22.9766 14.1387 22.7206 14.032C22.47 13.92 22.246 13.7813 22.0486 13.616C21.8513 13.4507 21.6913 13.248 21.5686 13.008C21.4513 12.7627 21.3926 12.4667 21.3926 12.12C21.3926 11.8107 21.4566 11.5147 21.5846 11.232C21.7126 10.944 21.8993 10.6933 22.1446 10.48C22.39 10.2613 22.6913 10.088 23.0486 9.96C23.406 9.832 23.814 9.768 24.2726 9.768C24.806 9.768 25.2833 9.85333 25.7046 10.024C26.1313 10.1893 26.4993 10.4187 26.8086 10.712L26.4886 11.232ZM27.6215 9.896H28.7815C28.8988 9.896 28.9948 9.92533 29.0695 9.984C29.1442 10.0427 29.1948 10.112 29.2215 10.192L31.2775 15.408C31.3415 15.6053 31.3975 15.7973 31.4455 15.984C31.4935 16.1707 31.5388 16.3573 31.5815 16.544C31.6242 16.3573 31.6695 16.1707 31.7175 15.984C31.7655 15.7973 31.8242 15.6053 31.8935 15.408L33.9735 10.192C34.0055 10.1067 34.0588 10.0373 34.1335 9.984C34.2082 9.92533 34.2962 9.896 34.3975 9.896H35.5095L32.2055 18H30.9255L27.6215 9.896Z" fill="#FCFDF8"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2036_4892">
|
||||
<rect width="42" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
<button class="user-info" ngbDropdownToggle *ngIf="getIsAuth()">{{getUserFullname()}}</button>
|
||||
<div ngbDropdownMenu *ngIf="getIsAuth()">
|
||||
<button class="user-info" ngbDropdownToggle *ngIf="isAuthenticated()">{{getUserFullname()}}</button>
|
||||
<div ngbDropdownMenu *ngIf="isAuthenticated()">
|
||||
<div class="user-department">{{getOrgUnitName()}}</div>
|
||||
<a routerLink="/mydata" class="data">Данные организации</a>
|
||||
<button ngbDropdownItem class="exit" (click)="logout()">Выйти</button>
|
||||
</div>
|
||||
<button class="exit" *ngIf="!getIsAuth()" (click)="logout()">Выйти</button>
|
||||
<button class="exit" *ngIf="!isAuthenticated()" (click)="logout()">Выйти</button>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from
|
|||
import {FileItem, FileUploader} from "ng2-file-upload";
|
||||
import {FileLikeObject} from "ng2-file-upload/file-upload/file-like-object.class";
|
||||
import {EmployeeInfoFileFormType} from "./EmployeeInfoFileFormType";
|
||||
import {CookieService} from "ngx-cookie";
|
||||
import {TokenConstants} from "../../../modules/security/TokenConstants";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
|
|
@ -59,6 +61,7 @@ export class ErvuFileUpload extends InputControl {
|
|||
private messagesService: MessagesService;
|
||||
private isUploadErrorOccurred = false;
|
||||
private appConfigService: AppConfigService;
|
||||
private cookieService: CookieService;
|
||||
|
||||
constructor(el: ElementRef, cd: ChangeDetectorRef) {
|
||||
super(el, cd);
|
||||
|
|
@ -69,6 +72,7 @@ export class ErvuFileUpload extends InputControl {
|
|||
super.initialize();
|
||||
this.messagesService = this.injector.get(MessagesService);
|
||||
this.appConfigService = this.injector.get(AppConfigService);
|
||||
this.cookieService = this.injector.get(CookieService);
|
||||
this.url = `/${this.appConfigService.getParamValue(ErvuFileUpload.BACKEND_URL)}/employee/document`;
|
||||
|
||||
this.uploader.setOptions({
|
||||
|
|
@ -99,6 +103,10 @@ export class ErvuFileUpload extends InputControl {
|
|||
{
|
||||
name: "Client-Time-Zone",
|
||||
value: Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
},
|
||||
{
|
||||
name: TokenConstants.CSRF_HEADER_NAME,
|
||||
value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME)
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
@ -129,6 +137,24 @@ export class ErvuFileUpload extends InputControl {
|
|||
|
||||
private setUploaderMethods() {
|
||||
this.uploader.onBeforeUploadItem = (fileItem: FileItem) => {
|
||||
|
||||
//refresh headers
|
||||
this.uploader.setOptions({
|
||||
headers: [
|
||||
{
|
||||
name: "X-Employee-Info-File-Form-Type",
|
||||
value: EmployeeInfoFileFormType[this.formType]
|
||||
},
|
||||
{
|
||||
name: "Client-Time-Zone",
|
||||
value: Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
},
|
||||
{
|
||||
name: TokenConstants.CSRF_HEADER_NAME,
|
||||
value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME)
|
||||
}
|
||||
]
|
||||
});
|
||||
this.fileUploadStartEvent.trigger();
|
||||
this.isDropZoneVisible = false;
|
||||
this.isFilesListVisible = false;
|
||||
|
|
@ -239,4 +265,4 @@ export class ErvuFileUpload extends InputControl {
|
|||
this.isUploadErrorOccurred = false;
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import {AnalyticalScope, Behavior, Container, ControlWithValue} from "@webbpm/ba
|
|||
import {HttpClient} from "@angular/common/http";
|
||||
import {OrgData} from "./OrgData";
|
||||
import {OrgInfoModel} from "../generated/ru/micord/ervu/security/esia/model/OrgInfoModel";
|
||||
import {CookieService} from "ngx-cookie";
|
||||
import {AuthenticationService} from "../modules/security/authentication.service";
|
||||
|
||||
@AnalyticalScope(Container)
|
||||
export class OrgDataRoot extends Behavior{
|
||||
export class OrgDataRoot extends Behavior {
|
||||
|
||||
private container: Container;
|
||||
|
||||
|
|
@ -14,8 +14,9 @@ export class OrgDataRoot extends Behavior{
|
|||
this.container = this.getScript(Container);
|
||||
let orgScripts: OrgData[] = this.container.getScriptsInThisAndChildren(OrgData);
|
||||
let httpClient = this.injector.get(HttpClient);
|
||||
let cookieService = this.injector.get(CookieService);
|
||||
if (cookieService.get("webbpm.ervu-lkrp-ul")) {
|
||||
let cookieService = this.injector.get(AuthenticationService);
|
||||
|
||||
if (cookieService.isAuthenticated()) {
|
||||
httpClient.get<OrgInfoModel>("esia/org")
|
||||
.toPromise()
|
||||
.then(orgInfoModel => {
|
||||
|
|
@ -23,9 +24,9 @@ export class OrgDataRoot extends Behavior{
|
|||
return;
|
||||
}
|
||||
for (let orgData of orgScripts) {
|
||||
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
|
||||
'component.ControlWithValue');
|
||||
control.setValue(orgInfoModel[orgData.dataId]);
|
||||
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
|
||||
'component.ControlWithValue');
|
||||
control.setValue(orgInfoModel[orgData.dataId]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {forwardRef, NgModule} from "@angular/core";
|
||||
import {APP_INITIALIZER, forwardRef, NgModule} from "@angular/core";
|
||||
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule, registerLocaleData} from "@angular/common";
|
||||
import localeRu from '@angular/common/locales/ru';
|
||||
|
|
@ -25,6 +25,7 @@ import {FileUploadModule} from "ng2-file-upload";
|
|||
import {ErvuFileUpload} from "../../ervu/component/fileupload/ErvuFileUpload";
|
||||
import {InMemoryStaticGrid} from "../../ervu/component/grid/InMemoryStaticGrid";
|
||||
import {ErvuDownloadFileButton} from "../../ervu/component/button/ErvuDownloadFileButton";
|
||||
import {AuthenticationService} from "../security/authentication.service";
|
||||
|
||||
registerLocaleData(localeRu);
|
||||
export const DIRECTIVES = [
|
||||
|
|
@ -39,6 +40,10 @@ export const DIRECTIVES = [
|
|||
forwardRef(() => InMemoryStaticGrid)
|
||||
];
|
||||
|
||||
export function checkAuthentication(authService: AuthenticationService): () => Promise<any> {
|
||||
return () => authService.checkAuthentication();
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
|
@ -60,6 +65,13 @@ export const DIRECTIVES = [
|
|||
DIRECTIVES
|
||||
],
|
||||
providers: [
|
||||
AuthenticationService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: checkAuthentication,
|
||||
deps: [AuthenticationService],
|
||||
multi: true,
|
||||
},
|
||||
{ provide: ProgressIndicationService, useClass: AppProgressIndicationService }
|
||||
],
|
||||
bootstrap: [],
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {ChangeDetectorRef, Component, OnInit} from "@angular/core";
|
||||
import {Router} from "@angular/router";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {CookieService} from "ngx-cookie";
|
||||
import {AuthenticationService} from "../../security/authentication.service";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
|
|
@ -13,13 +13,12 @@ export class LogOutComponent implements OnInit{
|
|||
private userFullname: string;
|
||||
private orgUnitName: string;
|
||||
|
||||
|
||||
constructor(private router: Router, private httpClient: HttpClient,
|
||||
private cookieService: CookieService, private cd: ChangeDetectorRef) {
|
||||
private authenticationService: AuthenticationService, private cd: ChangeDetectorRef) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
let isAuth = this.getIsAuth();
|
||||
let isAuth = this.authenticationService.isAuthenticated();
|
||||
if (isAuth) {
|
||||
Promise.all([
|
||||
this.httpClient.get<string>("esia/userfullname").toPromise(),
|
||||
|
|
@ -33,20 +32,18 @@ export class LogOutComponent implements OnInit{
|
|||
}
|
||||
|
||||
public logout(): void {
|
||||
this.httpClient.get<string>("esia/logout").toPromise().then(url => {
|
||||
window.open(url, "_self");
|
||||
})
|
||||
this.authenticationService.logout();
|
||||
}
|
||||
|
||||
public getUserFullname(): string {
|
||||
return this.userFullname;
|
||||
}
|
||||
|
||||
public getIsAuth(): boolean {
|
||||
return this.cookieService.get("webbpm.ervu-lkrp-ul") != null;
|
||||
public isAuthenticated(): boolean {
|
||||
return this.authenticationService.isAuthenticated();
|
||||
}
|
||||
|
||||
public getOrgUnitName(): string {
|
||||
return this.orgUnitName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
frontend/src/ts/modules/security/TokenConstants.ts
Normal file
4
frontend/src/ts/modules/security/TokenConstants.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export class TokenConstants {
|
||||
public static readonly CSRF_TOKEN_NAME = "XSRF-TOKEN";
|
||||
public static readonly CSRF_HEADER_NAME = "X-XSRF-TOKEN";
|
||||
}
|
||||
29
frontend/src/ts/modules/security/authentication.service.ts
Normal file
29
frontend/src/ts/modules/security/authentication.service.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {Observable} from "rxjs";
|
||||
import {CookieService} from "ngx-cookie";
|
||||
import {tap} from "rxjs/operators";
|
||||
import {AppConfigService} from "@webbpm/base-package";
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class AuthenticationService {
|
||||
|
||||
constructor(private http: HttpClient,
|
||||
private cookieService: CookieService,
|
||||
private appConfigService: AppConfigService) {
|
||||
}
|
||||
|
||||
checkAuthentication(): Promise<any>{
|
||||
return this.appConfigService.load().then(value => this.http.get<any>("version").toPromise())
|
||||
}
|
||||
|
||||
logout(): Promise<any> {
|
||||
return this.http.post<string>('esia/logout', {}, { responseType: 'text' as 'json' }).toPromise().then(url => {
|
||||
window.open(url, "_self");
|
||||
});
|
||||
}
|
||||
|
||||
public isAuthenticated(): boolean {
|
||||
return this.cookieService.get('webbpm.ervu-lkrp-ul') != null;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@ import {Injectable} from "@angular/core";
|
|||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
|
||||
import {Observable} from "rxjs";
|
||||
import {HttpClient, HttpParams} from "@angular/common/http";
|
||||
import {CookieService} from "ngx-cookie";
|
||||
import {MessagesService} from "@webbpm/base-package";
|
||||
import {AuthenticationService} from "../authentication.service";
|
||||
|
||||
@Injectable({providedIn:'root'})
|
||||
export abstract class AuthGuard implements CanActivate {
|
||||
|
|
@ -11,7 +11,7 @@ export abstract class AuthGuard implements CanActivate {
|
|||
protected constructor(
|
||||
protected router: Router,
|
||||
private httpClient: HttpClient,
|
||||
private cookieService: CookieService,
|
||||
private authenticationService: AuthenticationService,
|
||||
private messageService: MessagesService
|
||||
) {
|
||||
}
|
||||
|
|
@ -73,12 +73,8 @@ export abstract class AuthGuard implements CanActivate {
|
|||
});
|
||||
}
|
||||
|
||||
private checkAccess(): Promise<boolean> | boolean {
|
||||
return this.getIsAuth() != null;
|
||||
};
|
||||
|
||||
public getIsAuth(): string {
|
||||
return this.cookieService.get('webbpm.ervu-lkrp-ul');
|
||||
private checkAccess(): boolean {
|
||||
return this.authenticationService.isAuthenticated();
|
||||
}
|
||||
|
||||
private extractCode(message: string): string | null {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {CookieService} from "ngx-cookie";
|
||||
import {TokenConstants} from "../../security/TokenConstants";
|
||||
|
||||
@Injectable()
|
||||
export class AbsoluteUrlCsrfInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(private cookieService: CookieService) {
|
||||
}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
|
||||
let requestToForward = req;
|
||||
let token = this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) as string;
|
||||
|
||||
if (token != null) {
|
||||
let headers = {};
|
||||
let headerName = TokenConstants.CSRF_HEADER_NAME;
|
||||
headers[headerName] = token;
|
||||
requestToForward = req.clone({setHeaders: headers});
|
||||
}
|
||||
return next.handle(requestToForward);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,11 @@ import {
|
|||
HttpSecurityErrorInterceptor,
|
||||
HttpSecurityInterceptor
|
||||
} from "@webbpm/base-package";
|
||||
import {AbsoluteUrlCsrfInterceptor} from "./absolute-url-csrf.interceptor";
|
||||
|
||||
export const DEFAULT_HTTP_INTERCEPTOR_PROVIDERS = [
|
||||
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityErrorInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true}
|
||||
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: AbsoluteUrlCsrfInterceptor, multi: true}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import {HTTP_INTERCEPTORS} from "@angular/common/http";
|
||||
import {FormDirtyInterceptor, HttpSecurityInterceptor} from "@webbpm/base-package";
|
||||
import {DevHttpSecurityErrorInterceptor} from "./http-security-error-interceptor.dev";
|
||||
import {AbsoluteUrlCsrfInterceptor} from "./absolute-url-csrf.interceptor";
|
||||
|
||||
export const DEFAULT_HTTP_INTERCEPTOR_PROVIDERS = [
|
||||
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: DevHttpSecurityErrorInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true}
|
||||
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: AbsoluteUrlCsrfInterceptor, multi: true},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import {
|
|||
import {AppRoutingModule} from "../app/app-routing.module";
|
||||
import {GlobalErrorHandler} from "./handler/global-error.handler.prod";
|
||||
import {DEFAULT_HTTP_INTERCEPTOR_PROVIDERS} from "./interceptor/default-interceptors.prod";
|
||||
import {HttpClientModule, HttpClientXsrfModule} from "@angular/common/http";
|
||||
import {TokenConstants} from "../security/TokenConstants";
|
||||
|
||||
let IMPORTS = [
|
||||
BrowserAnimationsModule,
|
||||
|
|
@ -30,7 +32,10 @@ let IMPORTS = [
|
|||
CoreModule,
|
||||
ComponentsModule,
|
||||
AppModule,
|
||||
WebbpmRoutingModule
|
||||
WebbpmRoutingModule,
|
||||
HttpClientModule,
|
||||
HttpClientXsrfModule.withOptions(
|
||||
{cookieName: TokenConstants.CSRF_TOKEN_NAME, headerName: TokenConstants.CSRF_HEADER_NAME})
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
|||
23
pom.xml
23
pom.xml
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<version>1.8.2-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>backend</module>
|
||||
|
|
@ -26,6 +26,27 @@
|
|||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-bom</artifactId>
|
||||
<version>5.8.14</version>
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ru.cg.webbpm</groupId>
|
||||
<artifactId>platform-bom</artifactId>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<version>1.8.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue