65

私はこの質問が以前に尋ねられたことを知っています、しかし私はここで特定の問題に直面しています。

スプリングセキュリティ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>

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

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

4

9 に答える 9

49

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

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

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


場合によっては(正確なユースケースによっては) 、 (のスーパークラス)によって呼び出されるuseRefererフラグを有効にするだけで十分です。AbstractAuthenticationTargetUrlRequestHandlerSimpleUrlAuthenticationSuccessHandlerSavedRequestAwareAuthenticationSuccessHandler

<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>
于 2013-01-29T06:51:46.173 に答える
48

オルケイのいい答えを拡張したい。彼のアプローチは優れています。リファラー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として定義し、セキュリティ構成で使用する必要があります。アノテーション設定を使用している場合は、次のようになります(拡張元のクラス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
    ;
}
于 2014-05-22T01:24:10.753 に答える
8

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

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

@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);
}
于 2013-07-03T12:47:18.143 に答える
2

私はカスタムOAuth2認証をrequest.getHeader("Referer")使用しており、決定の時点では利用できません。しかし、セキュリティ要求はすでに保存されていExceptionTranslationFilter.sendStartAuthenticationます:

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

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

@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 && "GET".equals(savedRequest.getMethod()) ?  
    savedRequest.getRedirectUrl() : "defaultURL");
}
于 2019-06-03T20:50:51.010 に答える
0

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

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

Spring MVCコントローラーでは、redirectメソッドがセッションからパスを読み取り、を返す必要がありredirect:<my_saved_product_path>ます。

したがって、ユーザーがログインすると、ページに送信され/redirect、最後にアクセスした製品ページにユーザーがすぐにリダイレクトされます。

于 2013-07-23T01:45:58.160 に答える
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;
        }
}
于 2015-05-07T12:03:43.497 に答える
0

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クラスを拡張し、ユーザー定義のdetermineTargetUrl()メソッドによって返されるURLを使用して構成済みのRedirectStrategy(この場合はデフォルト)を使用してリダイレクトを呼び出すだけのhandle()メソッドをオーバーライドします。このメソッドは、認証オブジェクトから現在ログインしているユーザーのロールを抽出し、そこにあるロールに基づいて適切なURLを作成します。最後に、Spring Securityフレームワーク内のすべてのリダイレクトを担当するRedirectStrategyは、要求を指定された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]にリダイレクトします。

于 2016-06-14T16:38:48.800 に答える
0

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を提供してください。

@RequestMapping(value = "/login", params = "error")
public String loginError() {
    // Don't save referrer here!
}
于 2016-09-27T09:42:26.423 に答える
0

ユーザーの役割に関係なく特定のページにリダイレクトするには、Springの構成ファイルでdefaultSucessUrlを使用するだけです。

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    
      http.authorizeRequests() 
      .antMatchers("/admin").hasRole("ADMIN") 
      .and()
      .formLogin() .loginPage("/login") 
                    .defaultSuccessUrl("/admin",true)
      .loginProcessingUrl("/authenticateTheUser")
      .permitAll();
     
    
于 2020-08-27T12:01:36.937 に答える