This commit is contained in:
parent
fac2ca4278
commit
220caec7cc
9 changed files with 190 additions and 19 deletions
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -91,7 +91,7 @@ public class KeycloakRestTemplateAuthenticationProvider implements Authenticatio
|
||||||
final KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, skSession);
|
final KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, skSession);
|
||||||
KeycloakAuthenticationToken keycloakAuthenticationToken = new KeycloakAuthenticationToken(account, false, realmRoles);
|
KeycloakAuthenticationToken keycloakAuthenticationToken = new KeycloakAuthenticationToken(account, false, realmRoles);
|
||||||
keycloakAuthenticationToken.setAuthenticated(true);
|
keycloakAuthenticationToken.setAuthenticated(true);
|
||||||
checkRightsAndCreate(principalName, roles);
|
log.debug("login successful: login {}, roles {}", principalName, String.join(";", roles));
|
||||||
return keycloakAuthenticationToken;
|
return keycloakAuthenticationToken;
|
||||||
|
|
||||||
} catch (VerificationException vex) {
|
} 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
|
@Override
|
||||||
public boolean supports(Class<?> type) {
|
public boolean supports(Class<?> type) {
|
||||||
return UsernamePasswordAuthenticationToken.class.equals(type);
|
return UsernamePasswordAuthenticationToken.class.equals(type);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticatio
|
||||||
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
|
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
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.config.core.GrantedAuthorityDefaults;
|
||||||
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
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.NullAuthenticatedSessionStrategy;
|
||||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||||
import ru.spcex.clearing.backendapi.config.element.BackendApiSettings;
|
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
|
@KeycloakConfiguration
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
private final Boolean securityDisabled;
|
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.securityDisabled = settings.getSecurity().getAuthorizationDisabled();
|
||||||
|
this.loginSuccessHandler = loginSuccessHandler;
|
||||||
|
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -65,11 +71,11 @@ public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
||||||
anyReq.hasAnyRole("admin", "default-roles-master")
|
anyReq.hasAnyRole("admin", "default-roles-master")
|
||||||
.and()
|
.and()
|
||||||
.formLogin()
|
.formLogin()
|
||||||
.successHandler((req, res, auth) -> res.setStatus(HttpStatus.OK.value()))
|
.successHandler(loginSuccessHandler)
|
||||||
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
|
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
|
||||||
.and()
|
.and()
|
||||||
.logout()
|
.logout()
|
||||||
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
|
.logoutSuccessHandler(logoutSuccessHandler)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import ru.spcex.clearing.backendapi.controller.request.cud.utilities.UserAuthAction;
|
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.Consts;
|
||||||
import ru.spcex.clearing.platform.messaging.domain.cud.utilities.UserAuthRequest;
|
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 ru.spcex.platform.utils.log.ExceptionUtils;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
@ -31,7 +33,7 @@ public class UserAuthProcessor {
|
||||||
this.operator = operator;
|
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();
|
UserAuthAction authEvent = new UserAuthAction();
|
||||||
UserAuthRequest requestData = authEvent.getAuthRequest();
|
UserAuthRequest requestData = authEvent.getAuthRequest();
|
||||||
requestData.setUsername(identifier);
|
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() {
|
private String getServerAddress() {
|
||||||
if (serverAddress != null) {
|
if (serverAddress != null) {
|
||||||
return serverAddress;
|
return serverAddress;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ spring.main.web-application-type=servlet
|
||||||
|
|
||||||
backend-api.example-setting=test
|
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.login=dev
|
||||||
backend-api.hazelcast.password=dev-pass
|
backend-api.hazelcast.password=dev-pass
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,5 @@ public interface Consts {
|
||||||
String DESTINATION_SDF02_NEW = "s-df-02-new";
|
String DESTINATION_SDF02_NEW = "s-df-02-new";
|
||||||
|
|
||||||
String USER_AUTH_SUCCESS = "user-auth-success";
|
String USER_AUTH_SUCCESS = "user-auth-success";
|
||||||
|
String USER_LOGOUT_SUCCESS = "user-logout-success";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue