spring - إعادة توجيه أمان الربيع إلى الصفحة السابقة بعد تسجيل الدخول بنجاح




4 Answers

ما يحدث بعد أن تتم معالجة تسجيل الدخول (الذي تتم إعادة توجيه عنوان URL إليه) بواسطة AuthenticationSuccessHandler .

يتم استدعاء هذه الواجهة (فئة ملموسة تنفيذ هو SavedRequestAwareAuthenticationSuccessHandler ) بواسطة AbstractAuthenticationProcessingFilter أو أحد الفئات الفرعية الخاصة به مثل ( UsernamePasswordAuthenticationFilter ) في الطريقة successfulAuthentication SavedRequestAwareAuthenticationSuccessHandler .

ومن أجل الحصول على عملية إعادة توجيه أخرى في الحالة 3 ، يجب أن تقوم SavedRequestAwareAuthenticationSuccessHandler فرعية SavedRequestAwareAuthenticationSuccessHandler وجعلها تفعل ما تريد.

في بعض الأحيان (بالاعتماد على usecase الخاص بك بالضبط) يكفي لتمكين استخدام useReferer من AbstractAuthenticationTargetUrlRequestHandler الذي يتم استدعاؤه بواسطة SimpleUrlAuthenticationSuccessHandler (الدرجة SavedRequestAwareAuthenticationSuccessHandler من SavedRequestAwareAuthenticationSuccessHandler ).

<bean id="authenticationFilter"
      class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <property name="filterProcessesUrl" value="/login/j_spring_security_check" />
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationSuccessHandler">
        <bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
            <property name="useReferer" value="true"/>
        </bean>
    </property>
    <property name="authenticationFailureHandler">
        <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
            <property name="defaultFailureUrl" value="/login?login_error=t" />
        </bean>
    </property>
</bean>

أعلم أن هذا السؤال قد طُرح من قبل ، لكني أواجه مشكلة معينة هنا.

أستخدم أمان الربيع 3.1.3.

لدي 3 حالات تسجيل دخول محتملة في تطبيق الويب الخاص بي:

  1. تسجيل الدخول عبر صفحة تسجيل الدخول: OK.
  2. تسجيل الدخول عبر صفحة مقيدة: OK أيضًا.
  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>

الحل هنا واضح تمامًا: يُسمح بصفحة تسجيل الدخول فقط للمستخدمين المجهولين. بمجرد اتصال مستخدم ، يعيد معالج الأخطاء توجيهه إلى الصفحة الرئيسية.

لقد أجريت بعض الاختبارات ، ويبدو أن كل شيء يعمل بشكل جيد.




لدي حل بعد وعملت بالنسبة لي.

عند طلب صفحة تسجيل الدخول ، اكتب قيمة المُحيل إلى الجلسة:

@RequestMapping(value="/login", method = RequestMethod.GET)
public String login(ModelMap model,HttpServletRequest request) {

    String referrer = request.getHeader("Referer");
    if(referrer!=null){
        request.getSession().setAttribute("url_prior_login", referrer);
    }
    return "user/login";
}

بعد ذلك ، بعد نجاح تطبيق مخصص تسجيل الدخول من SavedRequestAwareAuthenticationSuccessHandler سيقوم بإعادة توجيه المستخدم إلى الصفحة السابقة:

HttpSession session = request.getSession(false);
if (session != null) {
    url = (String) request.getSession().getAttribute("url_prior_login");
}

إعادة توجيه المستخدم:

if (url != null) {
    response.sendRedirect(url);
}



الرجوع إلى الصفحة السابقة بعد تسجيل الدخول بنجاح ، يمكننا استخدام مدير المصادقة المخصص على النحو التالي:

<!-- enable use-expressions -->
    <http auto-config="true" use-expressions="true">
        <!-- src** matches: src/bar.c src/baz.c src/test/bartest.c-->
        <intercept-url pattern="/problemSolution/home/**" access="hasRole('ROLE_ADMIN')"/>
        <intercept-url pattern="favicon.ico" access="permitAll"/>
        <form-login
                authentication-success-handler-ref="authenticationSuccessHandler"
                always-use-default-target="true"
                login-processing-url="/checkUser"
                login-page="/problemSolution/index"

                default-target-url="/problemSolution/home"
                authentication-failure-url="/problemSolution/index?error"
                username-parameter="username"
                password-parameter="password"/>
        <logout logout-url="/problemSolution/logout"
                logout-success-url="/problemSolution/index?logout"/>
        <!-- enable csrf protection -->
        <csrf/>
    </http>

    <beans:bean id="authenticationSuccessHandler"
            class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
        <beans:property name="defaultTargetUrl" value="/problemSolution/home"/>
    </beans:bean>

    <!-- Select users and user_roles from database -->
    <authentication-manager>
        <authentication-provider user-service-ref="customUserDetailsService">
            <password-encoder hash="plaintext">
            </password-encoder>
        </authentication-provider>
    </authentication-manager>

CustomUserDetailsService class

@Service
public class CustomUserDetailsService implements UserDetailsService {

        @Autowired
        private UserService userService;

        public UserDetails loadUserByUsername(String userName)
                        throws UsernameNotFoundException {
                com.codesenior.telif.local.model.User domainUser = userService.getUser(userName);

                boolean enabled = true;
                boolean accountNonExpired = true;
                boolean credentialsNonExpired = true;
                boolean accountNonLocked = true;

                return new User(
                                domainUser.getUsername(),
                                domainUser.getPassword(),
                                enabled,
                                accountNonExpired,
                                credentialsNonExpired,
                                accountNonLocked,
                                getAuthorities(domainUser.getUserRoleList())
                );
        }

        public Collection<? extends GrantedAuthority> getAuthorities(List<UserRole> userRoleList) {
                return getGrantedAuthorities(getRoles(userRoleList));
        }

        public List<String> getRoles(List<UserRole> userRoleList) {

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

                for(UserRole userRole:userRoleList){
                        roles.add(userRole.getRole());
                }
                return roles;
        }

        public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
                List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

                for (String role : roles) {
                        authorities.add(new SimpleGrantedAuthority(role));
                }
                return authorities;
        }

}

فئة المستخدم

import com.codesenior.telif.local.model.UserRole;
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.User;
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 java.util.ArrayList;
import java.util.Collection;
import java.util.List;


@Service
public class CustomUserDetailsService implements UserDetailsService {

        @Autowired
        private UserService userService;

        public UserDetails loadUserByUsername(String userName)
                        throws UsernameNotFoundException {
                com.codesenior.telif.local.model.User domainUser = userService.getUser(userName);

                boolean enabled = true;
                boolean accountNonExpired = true;
                boolean credentialsNonExpired = true;
                boolean accountNonLocked = true;

                return new User(
                                domainUser.getUsername(),
                                domainUser.getPassword(),
                                enabled,
                                accountNonExpired,
                                credentialsNonExpired,
                                accountNonLocked,
                                getAuthorities(domainUser.getUserRoleList())
                );
        }

        public Collection<? extends GrantedAuthority> getAuthorities(List<UserRole> userRoleList) {
                return getGrantedAuthorities(getRoles(userRoleList));
        }

        public List<String> getRoles(List<UserRole> userRoleList) {

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

                for(UserRole userRole:userRoleList){
                        roles.add(userRole.getRole());
                }
                return roles;
        }

        public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
                List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

                for (String role : roles) {
                        authorities.add(new SimpleGrantedAuthority(role));
                }
                return authorities;
        }

}

فئة UserRole

@Entity
public class UserRole {
        @Id
        @GeneratedValue
        private Integer userRoleId;

        private String role;

        @ManyToMany(fetch = FetchType.LAZY, mappedBy = "userRoleList")
        @JsonIgnore
        private List<User> userList;

        public Integer getUserRoleId() {
                return userRoleId;
        }

        public void setUserRoleId(Integer userRoleId) {
                this.userRoleId= userRoleId;
        }

        public String getRole() {
                return role;
        }

        public void setRole(String role) {
                this.role= role;
        }

        @Override
        public String toString() {
                return String.valueOf(userRoleId);
        }

        public List<User> getUserList() {
                return userList;
        }

        public void setUserList(List<User> userList) {
                this.userList= userList;
        }
}



لقد وجدت أن حل Utku Özdemir يعمل إلى حد ما ، ولكنه نوع من هزيمة الغرض من الطلب المحفوظ لأن سمة الجلسة ستحظى بالأولوية على ذلك. وهذا يعني أن عمليات إعادة التوجيه إلى صفحات آمنة لن تعمل كما هو محدد - بعد تسجيل الدخول ، سيتم إرسالك إلى الصفحة التي كنت فيها بدلاً من هدف إعادة التوجيه. لذلك كبديل يمكنك استخدام إصدار تم تعديله من SavedRequestAwareAuthenticationSuccessHandler بدلاً من توسيعه. سيسمح لك ذلك بالتحكم بشكل أفضل في وقت استخدام سمة الجلسة.

هنا مثال:

private static class MyCustomLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws ServletException, IOException {
        SavedRequest savedRequest = requestCache.getRequest(request, response);

        if (savedRequest == null) {
            HttpSession session = request.getSession();
            if (session != null) {
                String redirectUrl = (String) session.getAttribute("url_prior_login");
                if (redirectUrl != null) {
                    session.removeAttribute("url_prior_login");
                    getRedirectStrategy().sendRedirect(request, response, redirectUrl);
                } else {
                    super.onAuthenticationSuccess(request, response, authentication);
                }
            } else {
                super.onAuthenticationSuccess(request, response, authentication);
            }

            return;
        }

        String targetUrlParameter = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl()
                || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
            requestCache.removeRequest(request, response);
            super.onAuthenticationSuccess(request, response, authentication);

            return;
        }

        clearAuthenticationAttributes(request);

        // Use the DefaultSavedRequest URL
        String targetUrl = savedRequest.getRedirectUrl();
        logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
        getRedirectStrategy().sendRedirect(request, response, targetUrl);
    }
}

أيضًا ، لا تريد حفظ المُحيل عند إخفاق المصادقة ، حيث سيكون المُحيل عندئذٍ صفحة تسجيل الدخول نفسها. لذا تحقق من وجود خطأ في المعلمة يدويًا أو قدم طلبًا منفصلًا مثلما هو موضح أدناه.

@RequestMapping(value = "/login", params = "error")
public String loginError() {
    // Don't save referrer here!
}



Related

spring redirect login spring-security