web-dev-qa-db-ja.com

ログインが成功した後、Spring Securityは前のページにリダイレクトします

この質問は以前に聞かれたことがありますが、ここで特定の問題に直面しています。

Spring Security 3.1.3を使用します。

Webアプリケーションで3つのログインケースがあります。

  1. ログインページからログインします:OK。
  2. 制限されたページからログインする:OK.
  3. 制限のないページからのログイン:OKではありません...「製品」ページには誰でもアクセスでき、ユーザーはログインしている場合はコメントを投稿できます。したがって、ユーザーが接続できるようにするために、ログインフォームが同じページに含まれています。

ケース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>

ここでの解決策は非常に明白です。ログインページは匿名ユーザーにのみ許可されます。ユーザーが接続されると、エラーハンドラーはユーザーをホームページにリダイレクトします。

私はいくつかのテストを行いましたが、すべてがうまく機能しているようです。

50

ログイン後(ユーザーがリダイレクトされるURL)は、AuthenticationSuccessHandlerによって処理されます。

このインターフェイス(それを実装する具体的なクラスはSavedRequestAwareAuthenticationSuccessHandlerです)は、AbstractAuthenticationProcessingFilterまたはメソッドUsernamePasswordAuthenticationFilter内の(successfulAuthentication)のようなサブクラスの1つによって呼び出されます。

したがって、ケース3で他のリダイレクトを行うには、SavedRequestAwareAuthenticationSuccessHandlerをサブクラス化し、必要な処理を行う必要があります。


時々(正確なユースケースに応じて)useRefererAbstractAuthenticationTargetUrlRequestHandlerのスーパークラス)によって呼び出されるSimpleUrlAuthenticationSuccessHandlerSavedRequestAwareAuthenticationSuccessHandlerフラグを有効にすれば十分です。

<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>
42
Ralph

Olcayのいい答えを拡張したい。彼のアプローチは優れています。リファラーURLをセッションに追加するには、ログインページコントローラーを次のようにする必要があります。

_@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として定義し、セキュリティ構成で使用する必要があります。 annotation configを使用している場合、次のようになります(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
    ;
}
_
37
Utku Özdemir

私は次の解決策を持っていますが、それは私のために働きました。

ログインページが要求されるたびに、セッションにリファラー値を書き込みます。

@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);
}
5
Olcay Tarazan

私はカスタムOAuth2認可を持っていますが、request.getHeader("Referer")は決定の時点で利用できません。ただし、セキュリティリクエストは既にExceptionTranslationFilter.handleSpringSecurityExceptionに保存されています:

protected void sendStartAuthentication(HttpServletRequest request,...
    ...
    requestCache.saveRequest(request, response);

したがって、必要なのは、Spring BeanとしてrequestCacheを共有することだけです。

@Bean
public RequestCache requestCache() {
   return new HttpSessionRequestCache();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
   http.authorizeRequests()
   ... 
   .requestCache().requestCache(requestCache()).and()
   ...
}     

承認が完了したときに使用します:

@Autowired
private RequestCache requestCache;

public void authenticate(HttpServletRequest req, HttpServletResponse resp){
    ....
    SavedRequest savedRequest = requestCache.getRequest(req, resp);
    resp.sendRedirect(savedRequest == null ? "defaultURL" : savedRequest.getRedirectUrl());
}
1
GKislin

次の一般的なソリューションは、通常のログイン、Spring Socialログイン、または他のほとんどのSpring Securityフィルターで使用できます。

Spring MVCコントローラーで、製品ページをロードするときに、ユーザーがログインしていない場合はセッションの製品ページへのパスを保存します。XML構成で、デフォルトのターゲットURLを設定します。例えば:

Spring MVCコントローラーでは、リダイレクトメソッドがセッションからパスを読み取り、redirect:<my_saved_product_path>

そのため、ユーザーがログインすると、それらは/redirect page。最後にアクセスした製品ページにすぐにリダイレクトします。

0

ログインに成功した後、前のページに戻ると、次のカスタム認証マネージャーを使用できます。

<!-- 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>

CustomUserDetailsS​​erviceクラス

@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;
        }
}
0
olyanren

SimpleUrlAuthenticationSuccessHandlerを拡張するカスタムSuccessHandlerを使用して、割り当てられた役割に応じてログインするときにユーザーを異なる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クラスを拡張し、ユーザー定義の決定ターゲットUrl()メソッドによって返されたURLで構成済みのRedirectStrategy [この場合はデフォルト]を使用してリダイレクトを単に呼び出すhandle()メソッドをオーバーライドします。このメソッドは、現在ログインしているユーザーのロールを認証オブジェクトから抽出し、そのロールに基づいて適切な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]に基づいてユーザーを[student/admin/staff]にリダイレクトします。

0

tkuÖ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を提供します。

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