Ich habe Spring-Boot-Anwendung, die auch Spring Security verwendet. Ich möchte überprüfen, ob Benutzer Zugriff auf die Anmeldung an der Anwendung haben, aber es muss nach der Authentifizierung sein. Der Punkt ist, dass der Benutzer bei der Anmeldung das Projekt auswählt, zu dem er/sie sich verbinden muss. Ein Benutzer kann sich mit einem Projekt verbinden, darf jedoch keine Verbindung zu einem anderen Projekt herstellen. Wenn der Benutzer jedoch ungültige Anmeldeinformationen eingibt, muss die Meldung zu ungültigen Anmeldeinformationen zuerst angezeigt werden, auch wenn der Benutzer nicht berechtigt ist, sich beim ausgewählten Projekt anzumelden. Aus diesem Grund muss die Überprüfung der Rechte für das Projekt nach der Authentifizierung erfolgen.Spring Boot Security - Wie wird HTTP 403 nach erfolgreicher Authentifizierung zurückgegeben, aber erfolglose Autorisierung?
Das ist mein SecurityConfig Klasse:
package org.aze.accountingprogram.config;
import org.aze.accountingprogram.models.CurrentUser;
import org.aze.accountingprogram.models.PermissionAliasConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/lib/**").permitAll().anyRequest().fullyAuthenticated()
.and()
.formLogin().successHandler(successHandler()).loginPage("/login").permitAll()
.and().exceptionHandling().accessDeniedHandler(accessDeniedHandler())
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll();
http.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new Md5PasswordEncoder());
}
private AccessDeniedHandler accessDeniedHandler() {
return (request, response, e) -> {
logger.debug("Returning HTTP 403 FORBIDDEN with message: \"{}\"", e.getMessage());
response.sendError(HttpStatus.FORBIDDEN.value(), e.getMessage());
};
}
private AuthenticationSuccessHandler successHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
CurrentUser user = (CurrentUser) authentication.getPrincipal();
if (!user.hasAccessRight(PermissionAliasConstants.LOGIN)) {
throw new AccessDeniedException(String.format("User \"%s\" is not authorized to login \"%s\" project", user.getUsername(), user.getProject().getName()));
}
}
};
}
}
Implementierung von UserDetailsService:
package org.aze.accountingprogram.serviceimpl;
import org.aze.accountingprogram.common.Constants;
import org.aze.accountingprogram.exceptions.DataNotFoundException;
import org.aze.accountingprogram.models.AccessRightsPermission;
import org.aze.accountingprogram.models.CurrentUser;
import org.aze.accountingprogram.models.Project;
import org.aze.accountingprogram.models.User;
import org.aze.accountingprogram.service.AccessRightsService;
import org.aze.accountingprogram.service.ProjectService;
import org.aze.accountingprogram.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Autowired
private ProjectService projectService;
@Autowired
private AccessRightsService accessRightsService;
@Autowired
private HttpServletRequest request;
private static final Logger logger = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user;
Project project;
final String projectId = request.getParameter(Constants.SESSION_PROJECT_ID);
logger.debug("Username: {}, projectId: {}", username, projectId);
try {
user = userService.getUserByUsername(username);
} catch (DataNotFoundException e) {
throw new UsernameNotFoundException(e.getMessage(), e);
}
// Value of projectId is from combo box which is filled from table of projects
// That is why there is no probability that the project will not be found
project = projectService.getProjectById(Integer.valueOf(projectId));
// User can have different rights for different projects
List<AccessRightsPermission> accessRights = accessRightsService.getAccessRightsByProject(user.getId(), project.getId());
Set<GrantedAuthority> authorities = new HashSet<>(accessRights.size());
authorities.addAll(accessRights.stream().map(right -> new SimpleGrantedAuthority(right.getAlias())).collect(Collectors.toList()));
final CurrentUser currentUser = new CurrentUser(user, project, authorities);
// If to check LOGIN access right to project here, and user entered right credentials
// then user will see message about invalid credentials.
// .and().exceptionHandling().accessDeniedHandler(accessDeniedHandler()) at SecurityConfig is not working in this case
// if (!currentUser.hasAccessRight(PermissionAliasConstants.LOGIN)) {
// throw new AccessDeniedException(String.format("User \"%s\" is not authorized to login \"%s\" project", user.getUsername(), project.getName()));
// }
logger.info("Logged in user: {}", currentUser);
return currentUser;
}
}
und LoginController
package org.aze.accountingprogram.controllers;
import org.springframework.stereotype.Controller;
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.servlet.ModelAndView;
import java.util.Optional;
@Controller
public class LoginController {
@RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView getLoginPage(@RequestParam Optional<String> error) {
return new ModelAndView("login", "error", error);
}
}
successHandler()
Werke und Anwendung wirft AccessDeniedException
wenn Nutzer hat nicht richtig zugreifen zu dem Projekt . Aber accessDeniedHandler()
funktioniert nicht und sendet nicht HTTP 403. Stattdessen erhalte ich HTTP 500.
Wie Antwort mit HTTP 403 und Nachricht der Ausnahme (zB "User" dural "ist nicht berechtigt, Login" AZB " Projekt ") und damit umgehen in LoginController
(mit @ResponseStatus(HttpStatus.FORBIDDEN)
oder @ExceptionHandler
)?
Ich habe nicht Handler von @RequestMapping (value = "/ login", method = RequestMethod.POST). Spring Security behandelt die POST-Anforderung an/login selbst. Ich konfigurierte Authentifizierung ähnlich Beispiel von hier: https://spring.io/guides/gs/securing-web/ – Tural
Aktualisierte die Antwort mit einer neuen Lösung, bitte überprüfen Sie es :) – niilzon
Ich versuchte diese Lösung vor dem Schreiben dieser Frage. 'loadUserByUsername' wird ausgeführt, bevor das Passwort überprüft wird. Wenn also in diesem Fall der Benutzer keinen Zugriff auf das Projekt hat und einen ungültigen Benutzernamen oder ein ungültiges Passwort eingegeben hat, sagt die Anwendung, dass der Benutzer nicht auf das Projekt zugreifen kann, sondern dass der Benutzer ungültige Anmeldedaten eingegeben hat. Dies bedeutet, dass die Autorisierung vor der Authentifizierung erscheint. – Tural