Spring Security在成功登錄後重定向到上一頁


Answers

我想擴展Olcay的很好的答案。 他的方法是好的,你的登錄頁面控制器應該是這樣的推薦人網址進入會議:

@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(HttpServletRequest request, Model model) {
    String referrer = request.getHeader("Referer");
    request.getSession().setAttribute("url_prior_login", referrer);
    // some other stuff
    return "login";
}

你應該擴展SavedRequestAwareAuthenticationSuccessHandler並重寫它的onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)方法。 像這樣的東西:

public class MyCustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    public MyCustomLoginSuccessHandler(String defaultTargetUrl) {
        setDefaultTargetUrl(defaultTargetUrl);
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        HttpSession session = request.getSession();
        if (session != null) {
            String redirectUrl = (String) session.getAttribute("url_prior_login");
            if (redirectUrl != null) {
                // we do not forget to clean this attribute from session
                session.removeAttribute("url_prior_login");
                // then we redirect
                getRedirectStrategy().sendRedirect(request, response, redirectUrl);
            } else {
                super.onAuthenticationSuccess(request, response, authentication);
            }
        } else {
            super.onAuthenticationSuccess(request, response, authentication);
        }
    }
}

然後,在您的spring配置中,您應該將此自定義類定義為一個bean,並將其用於您的安全配置。 如果您使用註釋配置 ,它應該看起來像這樣(您從WebSecurityConfigurerAdapter擴展的類):

@Bean
public AuthenticationSuccessHandler successHandler() {
    return new MyCustomLoginSuccessHandler("/yourdefaultsuccessurl");
}

configure方法中:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            // bla bla
            .formLogin()
                .loginPage("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler(successHandler())
                .permitAll()
            // etc etc
    ;
}
Question

我知道這個問題之前已經被問過了,但是我在這裡面臨著一個特殊的問題。

我使用彈簧安全3.1.3。

我的Web應用程序中有3個可能的登錄情況:

  1. 通過登錄頁面登錄:OK。
  2. 通過受限頁面登錄:也可以。
  3. 通過非限制性頁面登錄:不行...每個人都可以訪問“產品”頁面,如果用戶登錄,用戶可以發表評論。 因此,登錄表單被包含在同一頁面中以允許用戶連接。

情況3)的問題是我無法將用戶重定向到“產品”頁面。 無論如何,他們會在成功登錄後重定向到主頁。

請注意,對於情況2)成功登錄後,重定向到受限制頁面的方式是開箱即用的。

這是我的security.xml文件的相關部分:

<!-- Authentication policy for the restricted page  -->
<http use-expressions="true" auto-config="true" pattern="/restrictedPage/**">
    <form-login login-page="/login/restrictedLogin" authentication-failure-handler-ref="authenticationFailureHandler" />
    <intercept-url pattern="/**" access="isAuthenticated()" />
</http>

<!-- Authentication policy for every page -->
<http use-expressions="true" auto-config="true">
    <form-login login-page="/login" authentication-failure-handler-ref="authenticationFailureHandler" />
    <logout logout-url="/logout" logout-success-url="/" />
</http>

我懷疑“每個頁面的身份驗證策略”是對這個問題負責。 但是,如果我刪除它,我不能再登錄... j_spring_security_check發送404錯誤。

編輯:

感謝拉爾夫,我能找到一個解決方案。 所以這是事情:我使用的財產

<property name="useReferer" value="true"/>

拉爾夫給我看。 之後,我遇到了一個問題1):通過登錄頁面進行登錄時,用戶停留在同一頁面(而不是重定向到主頁,就像以前一樣)。 直到這個階段的代碼如下:

<!-- Authentication policy for login page -->
<http use-expressions="true" auto-config="true" pattern="/login/**">
    <form-login login-page="/login" authentication-success-handler-ref="authenticationSuccessHandlerWithoutReferer" />
</http>

<!-- Authentication policy for every page -->
<http use-expressions="true" auto-config="true">
    <form-login login-page="/login" authentication-failure-handler-ref="authenticationFailureHandler" />
    <logout logout-url="/logout" logout-success-url="/" authentication-success-handler-ref="authenticationSuccessHandler"/>
</http>

<beans:bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <!-- After login, return to the last visited page -->
    <beans:property name="useReferer" value="true" />
</beans:bean>

<beans:bean id="authenticationSuccessHandlerWithoutReferer" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <!-- After login, stay to the same page -->
    <beans:property name="useReferer" value="false" />
</beans:bean>

至少在理論上這應該起作用,但事實並非如此。 我仍然不知道為什麼,所以如果有人有這個答案,我會很樂意創造一個新的話題,讓他分享他的解決方案。

與此同時,我來了一個解決方法。 不是最好的解決辦法,但正如我所說,如果有人有更好的展示,我全部耳朵。 所以這是登錄頁面的新認證策略:

<http use-expressions="true" auto-config="true" pattern="/login/**" >
    <intercept-url pattern="/**" access="isAnonymous()" />
    <access-denied-handler error-page="/"/>
</http>

這裡的解決方案非常明顯:登錄頁面只允許匿名用戶使用。 一旦用戶連接,錯誤處理程序將他重定向到主頁。

我做了一些測試,一切似乎都很好。




以下通用解決方案可以用於常規登錄,Spring社交登錄或大多數其他Spring Security過濾器。

在您的Spring MVC控制器中,加載產品頁面時,如果用戶尚未登錄,請將會話路徑保存到會話中。在XML配置中,設置默認目標URL。 例如:

在你的Spring MVC控制器中,重定向方法應該從會話中讀出路徑並返回redirect:<my_saved_product_path>

所以,用戶登錄後,他們將被發送到/redirect頁面,這將立即將他們重定向到他們上次訪問的產品頁面。




您可以使用Custom SuccessHandler擴展SimpleUrlAuthenticationSuccessHandler,以根據其分配的角色登錄時將用戶重定向到不同的URL。

CustomSuccessHandler類提供自定義重定向功能:

package com.mycompany.uomrmsweb.configuration;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        String targetUrl = determineTargetUrl(authentication);

        if (response.isCommitted()) {
            System.out.println("Can't redirect");
            return;
        }

        redirectStrategy.sendRedirect(request, response, targetUrl);
    }

    protected String determineTargetUrl(Authentication authentication) {
        String url="";

        Collection<? extends GrantedAuthority> authorities =  authentication.getAuthorities();

        List<String> roles = new ArrayList<String>();

        for (GrantedAuthority a : authorities) {
            roles.add(a.getAuthority());
        }

        if (isStaff(roles)) {
            url = "/staff";
        } else if (isAdmin(roles)) {
            url = "/admin";
        } else if (isStudent(roles)) {
            url = "/student";
        }else if (isUser(roles)) {
            url = "/home";
        } else {
            url="/Access_Denied";
        }

        return url;
    }

    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }
    protected RedirectStrategy getRedirectStrategy() {
        return redirectStrategy;
    }

    private boolean isUser(List<String> roles) {
        if (roles.contains("ROLE_USER")) {
            return true;
        }
        return false;
    }

    private boolean isStudent(List<String> roles) {
        if (roles.contains("ROLE_Student")) {
            return true;
        }
        return false;
    }

    private boolean isAdmin(List<String> roles) {
        if (roles.contains("ROLE_SystemAdmin") || roles.contains("ROLE_ExaminationsStaff")) {
            return true;
        }
        return false;
    }

    private boolean isStaff(List<String> roles) {
        if (roles.contains("ROLE_AcademicStaff") || roles.contains("ROLE_UniversityAdmin")) {
            return true;
        }
        return false;
    }
}

擴展Spring SimpleUrlAuthenticationSuccessHandler類和重寫handle()方法,該方法使用配置的RedirectStrategy(此例中為默認值)與用戶定義的determineTargetUrl()方法返回的URL調用重定向。 此方法從認證對像中提取當前登錄用戶的角色,然後根據角色構建適當的URL。 最後,RedirectStrategy負責Spring Security框架內的所有重定向,將請求重定向到指定的URL。

使用SecurityConfiguration類註冊CustomSuccessHandler:

package com.mycompany.uomrmsweb.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.userdetails.UserDetailsService;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("customUserDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    CustomSuccessHandler customSuccessHandler;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/", "/home").access("hasRole('USER')")
        .antMatchers("/admin/**").access("hasRole('SystemAdmin') or hasRole('ExaminationsStaff')")
        .antMatchers("/staff/**").access("hasRole('AcademicStaff') or hasRole('UniversityAdmin')")
        .antMatchers("/student/**").access("hasRole('Student')")  
                    .and().formLogin().loginPage("/login").successHandler(customSuccessHandler)
        .usernameParameter("username").passwordParameter("password")
        .and().csrf()
        .and().exceptionHandling().accessDeniedPage("/Access_Denied");
    }
}

successHandler是負責基於任何自定義邏輯的最終重定向的類,在這種情況下,根據他的角色[USER / Student / SystemAdmin / UniversityAdmin / ExaminationsStaff / AcademicStaff]來重定向用戶[給學生/管理員/員工]。