web-dev-qa-db-fra.com

Comment rediriger vers la page d'accueil si l'utilisateur accède à la page de connexion après s'être connecté?

Voici ma config de sécurité de printemps:

<http pattern="/auth/login" security="none" />
<http pattern="/auth/loginFailed" security="none" />
<http pattern="/resources/**" security="none" />

<http auto-config="true" access-decision-manager-ref="accessDecisionManager">
    <intercept-url pattern="/auth/logout" access="permitAll"/>
    <intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS"/>
    <intercept-url pattern="/**" access="XYZ_ACCESS"/>

    <form-login
        login-page="/auth/login"
        authentication-failure-url="/auth/loginFailed"
        authentication-success-handler-ref="authenticationSuccessHandler" />
    <logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>

La authenticationSuccessHandler étend la SavedRequestAwareAuthenticationSuccessHandler en s'assurant que l'utilisateur est redirigé vers la page qu'il a demandée à l'origine.

Cependant, étant donné que /auth/login est marqué comme security="none", je ne parviens pas à rediriger correctement l'utilisateur vers la page d'accueil s'il accède à la page de connexion après s'être connecté. Je pense que c'est la bonne expérience utilisateur aussi.

J'ai aussi essayé ci-dessous, mais l'objet Principal est toujours null, probablement à nouveau à cause de l'attribut security="none".

@RequestMapping(value = "/auth/login", method = GET)
public String showLoginForm(HttpServletRequest request, Principal principal) {
    if(principal != null) {
        return "redirect:/";
    }

    return "login";
}
17
adarshr

J'ai vérifié le sujet plus en profondeur que la dernière fois et j'ai constaté que vous devez déterminer si l'utilisateur est authentifié par vous-même dans le contrôleur. Row Winch (Spring Security dev) dit ici :

Spring Security n’a pas connaissance des éléments internes de votre application (c’est-à-dire si vous souhaitez assouplir votre page de connexion si l’utilisateur est connecté ou non). Pour afficher votre page d'accueil lorsque la page de connexion est demandé et que l'utilisateur est connecté, utilisez la variable SecurityContextHolder dans la page de connexion (ou son contrôleur) et rediriger ou transférer l'utilisateur vers la page d'accueil.

La solution serait donc de déterminer si l'utilisateur demandant /auth/login est anonyme ou non, comme ci-dessous.

applicationContext-security.xml:

<http auto-config="true" use-expressions="true"
        access-decision-manager-ref="accessDecisionManager">
    <intercept-url pattern="/auth/login" access="permitAll" />
    <intercept-url pattern="/auth/logout" access="permitAll" />
    <intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS" />
    <intercept-url pattern="/**" access="XYZ_ACCESS" />

    <form-login login-page="/auth/login"
        authentication-failure-url="/auth/loginFailed"
        authentication-success-handler-ref="authenticationSuccessHandler" />
    <logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>

<beans:bean id="defaultTargetUrl" class="Java.lang.String">
    <beans:constructor-arg value="/content" />
</beans:bean>

<beans:bean id="authenticationTrustResolver"
        class="org.springframework.security.authentication.AuthenticationTrustResolverImpl" />

<beans:bean id="authenticationSuccessHandler"
        class="com.example.spring.security.MyAuthenticationSuccessHandler">
    <beans:property name="defaultTargetUrl" ref="defaultTargetUrl" />
</beans:bean>

Ajoutez à la définition du bean applicationContext.xml:

<bean id="securityContextAccessor"
    class="com.example.spring.security.SecurityContextAccessorImpl" />

qui est classe

public final class SecurityContextAccessorImpl
      implements SecurityContextAccessor {

  @Autowired
  private AuthenticationTrustResolver authenticationTrustResolver;

  @Override
  public boolean isCurrentAuthenticationAnonymous() {
    final Authentication authentication =
        SecurityContextHolder.getContext().getAuthentication();
    return authenticationTrustResolver.isAnonymous(authentication);
  }
}

mise en place d'une interface simple

public interface SecurityContextAccessor {
  boolean isCurrentAuthenticationAnonymous();
}

( SecurityContextHolder code d'accès est découplé du contrôleur, j'ai suivi la suggestion de cette réponse , d'où SecurityContextAccessor interface.)

Et dernier mais non le moindre, rediriger la logique dans le contrôleur:

@Controller
@RequestMapping("/auth")
public class AuthController {
  @Autowired
  SecurityContextAccessor securityContextAccessor;

  @Autowired
  @Qualifier("defaultTargetUrl")
  private String defaultTargetUrl;

  @RequestMapping(value = "/login", method = RequestMethod.GET)
  public String login() {
    if (securityContextAccessor.isCurrentAuthenticationAnonymous()) {
      return "login";
    } else {
      return "redirect:" + defaultTargetUrl;
    }
  }
}

Définir defaultTargetUrl String bean semble être un hack, mais je n'ai pas de meilleur moyen de ne pas coder en dur l'url ... (En fait, dans notre projet, nous utilisons <util:constant> avec une classe contenant des champs String statiques.) après tout.

9
Xaerxess

Vous pouvez également limiter votre page de connexion à ROLE_ANONYMOUS et définir un <access-denied-handler />:

<access-denied-handler ref="accessDeniedHandler" />
<intercept-url pattern="/auth/login" access="ROLE_ANONYMOUS" />

Et dans votre gestionnaire, vérifiez si l'utilisateur est déjà authentifié:

@Service
public class AccessDeniedHandler extends AccessDeniedHandlerImpl {
    private final String HOME_PAGE = "/index.html";

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && !(auth instanceof AnonymousAuthenticationToken)) {
            response.sendRedirect(HOME_PAGE);
        }

        super.handle(request, response, e);
    }
}
5
dtrunk

Implémentez un intercepteur de redirection à cette fin:

L'intercepteur (implémentant HandlerInterceptor interface) vérifie si quelqu'un tente d'accéder à la page de connexion et si cette personne est déjà connectée, l'intercepteur envoie une redirection vers la page d'index.

public class LoginPageRedirectInterceptor extends HandlerInterceptorAdapter {    

    private String[] loginPagePrefixes = new String[] { "/login" };

    private String redirectUrl = "/index.html";

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        if (isInLoginPaths(this.urlPathHelper.getLookupPathForRequest(request))
                           && isAuthenticated()) {
            response.setContentType("text/plain");
            sendRedirect(request, response);
            return false;
        } else {
            return true;
        }
    }

    private boolean isAuthenticated() {
        Authentication authentication =
                        SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return false;
        }
        if (authentication instanceof AnonymousAuthenticationToken) {
            return false;
        }
        return authentication.isAuthenticated();
    }

    private void sendRedirect(HttpServletRequest request,
                              HttpServletResponse response) {

        String encodedRedirectURL = response.encodeRedirectURL(
                                 request.getContextPath() + this.redirectUrl);
        response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
        response.setHeader("Location", encodedRedirectURL);
    }

    private boolean isInLoginPaths(final String requestUrl) {   
        for (String login : this.loginPagePrefixes) {
            if (requestUrl.startsWith(login)) {
                return true;
            }
        }
        return false;
    }
}
4
Ralph

Vous pouvez conserver le flux simple par l'attribut access-denied-page dans l'élément http ou sous la forme dtrunk dit d'écrire gestionnaire pour l'accès refusé ainsi que. la config serait comme

<http access-denied-page="/403" ... >
 <intercept-url pattern="/login" access="ROLE_ANONYMOUS" />
 <intercept-url pattern="/user/**" access="ROLE_USER" />
 <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
 <form-login login-page="/login" default-target-url="/home" ... />
 ...
</http>

dans le contrôleur pour /403

@RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied() { //simple impl
    return "redirect:/home";
}

et pour /home

@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Authentication authentication) {
 // map as many home urls with Role
    Map<String, String> dashBoardUrls = new HashMap<String, String>();
    dashBoardUrls.put("ROLE_USER", "/user/dashboard");
    dashBoardUrls.put("ROLE_ADMIN", "/admin/dashboard");

    String url = null;

    Collection<? extends GrantedAuthority> grants = authentication
            .getAuthorities();
 // for one role per user
    for (GrantedAuthority grantedAuthority : grants) {
        url = dashBoardUrls.get(grantedAuthority.getAuthority());
    }
    if (url == null)
        return "/errors/default_access_denied.jsp";

    return "redirect:" + url;
}

et lorsque vous faites une demande pour /admin/dashboard sans être connecté, il redirigera /login automatiquement par sécurité

0
Naveen raj
<http pattern="/login" auto-config="true" disable-url-rewriting="true">
  <intercept-url pattern="/login" access="ROLE_ANONYMOUS"/>
  <access-denied-handler error-page="/index.jsp"/>
</http>
0
Fedor Mishin