2016-04-05 15 views
1

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)?

Antwort

0

Nicht sicher, ob dies das ist, was Sie suchen, aber Sie können die HttpServletResponse in der Login-POST-Methode Ihres Controllers injizieren.

Deshalb, wenn Ihr Service benachrichtigt Sie, dass die Genehmigung nicht in Ordnung ist, können Sie zum Beispiel tun

response.setStatus (403);

@RequestMapping(value = "/login", method = RequestMethod.POST) 
public ModelAndView loginAction(@RequestParam String login, @RequestParam String pw, HttpServletResponse response) { 
    doStuff(login,pw); 
    if(service.isNotHappy(login,pw)){ 
     response.setStatus(403); 
    }else{ 
     // happy flow 
    } 
    ... 
} 

// UPDATE:

seit dem

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

Methode ist die Umsetzung der org.springframework.security.core.userdetails.UserDetailsService Schnittstelle, in der Tat kann man nicht spritzen den HttpServletResponse, wie Sie es in einem @RequestMapping kommentiert Regler.

Hier ist jedoch ein andere (hoffentlich richtig!) Lösung:

1) Bei der Durchführung der loadUserByUsername(String x) Methode, wenn der Benutzer die Zugriffsrechte nicht hat, wirft ein CustomYourException

2) Erstellen eine neue ExceptionHandlingController Klasse mit @ControllerAdvice bei class level kommentierten (und stellen sie sicher, dass es wie Ihre Controller abgetastet wird), das eine Methode wie folgt enthält:

@ControllerAdvice 
public class ExceptionHandlingController { 

    @ExceptionHandler({CustomYourException.class}) 
    public ModelAndView handleCustomExceptionError(HttpServletRequest request, HttpServletResponse response, CustomYourException cyee) { 
     // this method will be triggered upon CustomYourException only 
     // you can manipulate the response code here, 
     // return a custom view, use a special logger 
     // or do whatever else you want here :) 
    } 
} 

Wie Sie sehen, wird dieser Handler basierend darauf aufgerufen, welche spezifischen Ausnahmen Sie in der @ExceptionHandler Annotation definieren - sehr nützlich! Von dort aus können Sie die HttpServletResponse bearbeiten und den gewünschten Antwortcode festlegen und/oder den Benutzer an eine bestimmte Ansicht senden.

Hope this löst Ihr Problem :)

+0

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

+0

Aktualisierte die Antwort mit einer neuen Lösung, bitte überprüfen Sie es :) – niilzon

+0

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

Verwandte Themen