ialbert 2022-09-20 16:32:47 +03:00
parent fac2ca4278
commit 220caec7cc
9 changed files with 190 additions and 19 deletions

View file

@ -0,0 +1,28 @@
package ru.spcex.clearing.backendapi.controller.request.cud.utilities;
import ru.spcex.clearing.backendapi.domain.actions.IAction;
import ru.spcex.clearing.platform.messaging.domain.ActionType;
import ru.spcex.clearing.platform.messaging.domain.cud.utilities.UserLogoutRequest;
public class UserLogoutAction implements IAction<UserLogoutRequest> {
private final UserLogoutRequest authRequest;
public UserLogoutAction() {
this.authRequest = new UserLogoutRequest();
}
public UserLogoutRequest getAuthRequest() {
return authRequest;
}
@Override
public UserLogoutRequest toRequest() {
return authRequest;
}
@Override
public ActionType getActionType() {
return ActionType.SYSTEM;
}
}

View file

@ -91,7 +91,7 @@ public class KeycloakRestTemplateAuthenticationProvider implements Authenticatio
final KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, skSession);
KeycloakAuthenticationToken keycloakAuthenticationToken = new KeycloakAuthenticationToken(account, false, realmRoles);
keycloakAuthenticationToken.setAuthenticated(true);
checkRightsAndCreate(principalName, roles);
log.debug("login successful: login {}, roles {}", principalName, String.join(";", roles));
return keycloakAuthenticationToken;
} catch (VerificationException vex) {
@ -101,17 +101,6 @@ public class KeycloakRestTemplateAuthenticationProvider implements Authenticatio
}
}
private void checkRightsAndCreate(String username, Set<String> roles) {
log.debug("login successful: login {}, roles {}", username, String.join(";", roles));
//todo remove hardcode
if (roles.contains("admin") || roles.contains("default-roles-master")) {
String clientIp = currentHttpRequest != null ? currentHttpRequest.getRemoteAddr() : null;
String serverIp = currentHttpRequest != null ? currentHttpRequest.getLocalAddr() : null;
userAutoCreation.sendUserInfo(username, roles, clientIp, serverIp);
}
}
@Override
public boolean supports(Class<?> type) {
return UsernamePasswordAuthenticationToken.class.equals(type);

View file

@ -5,7 +5,6 @@ import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticatio
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@ -14,19 +13,26 @@ import org.springframework.security.config.annotation.web.configurers.Expression
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import ru.spcex.clearing.backendapi.config.element.BackendApiSettings;
import ru.spcex.clearing.backendapi.security.handlers.AuthAuditSuccessLoginHandler;
import ru.spcex.clearing.backendapi.security.handlers.LogoutAuditSuccessHandler;
@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private final Boolean securityDisabled;
private final AuthAuditSuccessLoginHandler loginSuccessHandler;
private final LogoutAuditSuccessHandler logoutSuccessHandler;
public WebSecurityConfig(BackendApiSettings settings) {
public WebSecurityConfig(BackendApiSettings settings,
AuthAuditSuccessLoginHandler loginSuccessHandler,
LogoutAuditSuccessHandler logoutSuccessHandler) {
this.securityDisabled = settings.getSecurity().getAuthorizationDisabled();
this.loginSuccessHandler = loginSuccessHandler;
this.logoutSuccessHandler = logoutSuccessHandler;
}
@Override
@ -65,11 +71,11 @@ public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
anyReq.hasAnyRole("admin", "default-roles-master")
.and()
.formLogin()
.successHandler((req, res, auth) -> res.setStatus(HttpStatus.OK.value()))
.successHandler(loginSuccessHandler)
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
.logoutSuccessHandler(logoutSuccessHandler)
;
}
}

View file

@ -0,0 +1,51 @@
package ru.spcex.clearing.backendapi.security.handlers;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Service;
import ru.spcex.clearing.backendapi.service.UserAuthProcessor;
import ru.spcex.platform.utils.log.ExceptionUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;
@Service
public class AuthAuditSuccessLoginHandler implements AuthenticationSuccessHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
private final UserAuthProcessor userAutoCreation;
@Autowired
public AuthAuditSuccessLoginHandler(UserAuthProcessor userAutoCreation) {
this.userAutoCreation = userAutoCreation;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
try {
KeycloakAuthenticationToken auth = (KeycloakAuthenticationToken) authentication;
KeycloakAccount details = (KeycloakAccount) auth.getDetails();
String username = ((KeycloakPrincipal) details.getPrincipal())
.getKeycloakSecurityContext()
.getToken()
.getPreferredUsername();
Set<String> roles = details.getRoles();
if (roles.contains("admin") || roles.contains("default-roles-master")) {
String clientIp = request != null ? request.getRemoteAddr() : null;
String serverIp = request != null ? request.getLocalAddr() : null;
userAutoCreation.sendAuthSuccessEvent(username, roles, clientIp, serverIp);
}
} catch (Throwable e) {
log.error(ExceptionUtils.getStackTrace(e));
}
response.setStatus(HttpStatus.OK.value());
}
}

View file

@ -0,0 +1,48 @@
package ru.spcex.clearing.backendapi.security.handlers;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.stereotype.Service;
import ru.spcex.clearing.backendapi.service.UserAuthProcessor;
import ru.spcex.platform.utils.log.ExceptionUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Service
public class LogoutAuditSuccessHandler extends HttpStatusReturningLogoutSuccessHandler {
private final UserAuthProcessor logoutEventSender;
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
public LogoutAuditSuccessHandler(UserAuthProcessor logoutEventSender) {
super(HttpStatus.OK);
this.logoutEventSender = logoutEventSender;
}
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException {
try {
KeycloakAuthenticationToken auth = (KeycloakAuthenticationToken) authentication;
KeycloakAccount details = (KeycloakAccount) auth.getDetails();
String username = ((KeycloakPrincipal) details.getPrincipal())
.getKeycloakSecurityContext()
.getToken()
.getPreferredUsername();
this.logoutEventSender.sendLogoutSuccessEvent(username);
} catch (Throwable e) {
log.error(ExceptionUtils.getStackTrace(e));
}
super.onLogoutSuccess(request, response, authentication);
}
}

View file

@ -6,8 +6,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import ru.spcex.clearing.backendapi.controller.request.cud.utilities.UserAuthAction;
import ru.spcex.clearing.backendapi.controller.request.cud.utilities.UserLogoutAction;
import ru.spcex.clearing.platform.messaging.domain.Consts;
import ru.spcex.clearing.platform.messaging.domain.cud.utilities.UserAuthRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.utilities.UserLogoutRequest;
import ru.spcex.platform.utils.log.ExceptionUtils;
import java.net.InetAddress;
@ -31,7 +33,7 @@ public class UserAuthProcessor {
this.operator = operator;
}
public void sendUserInfo(String identifier, Collection<String> roles, String userIp, String serverIp) {
public void sendAuthSuccessEvent(String identifier, Collection<String> roles, String userIp, String serverIp) {
UserAuthAction authEvent = new UserAuthAction();
UserAuthRequest requestData = authEvent.getAuthRequest();
requestData.setUsername(identifier);
@ -46,6 +48,18 @@ public class UserAuthProcessor {
}
}
public void sendLogoutSuccessEvent(String identifier) {
UserLogoutAction logoutEvent = new UserLogoutAction();
UserLogoutRequest requestData = logoutEvent.getAuthRequest();
requestData.setUsername(identifier);
requestData.setTime(Instant.now());
try {
operator.sendRequestToQueue(Consts.USER_LOGOUT_SUCCESS, logoutEvent);
} catch (Throwable e) {
log.error(ExceptionUtils.getStackTrace(e));
}
}
private String getServerAddress() {
if (serverAddress != null) {
return serverAddress;

View file

@ -4,7 +4,7 @@ spring.main.web-application-type=servlet
backend-api.example-setting=test
backend-api.hazelcast.cluster-members=127.0.0.1
backend-api.hazelcast.cluster-members=127.0.0.1:5701
backend-api.hazelcast.login=dev
backend-api.hazelcast.password=dev-pass

View file

@ -24,4 +24,5 @@ public interface Consts {
String DESTINATION_SDF02_NEW = "s-df-02-new";
String USER_AUTH_SUCCESS = "user-auth-success";
String USER_LOGOUT_SUCCESS = "user-logout-success";
}

View file

@ -0,0 +1,34 @@
package ru.spcex.clearing.platform.messaging.domain.cud.utilities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import ru.spcex.clearing.platform.messaging.domain.json.deserialize.InstantDeserializer;
import ru.spcex.clearing.platform.messaging.domain.json.serialize.InstantSerializer;
import java.time.Instant;
public class UserLogoutRequest {
@JsonProperty
private String username;
@JsonSerialize(using = InstantSerializer.class)
@JsonDeserialize(using = InstantDeserializer.class)
@JsonProperty
private Instant time;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Instant getTime() {
return time;
}
public void setTime(Instant time) {
this.time = time;
}
}