从单个登录表单入口点启动Spring多个身份验证提供程序

我有一个Spring Boot 2.2应用程序,用于使用组织的CAS实例对用户进行身份验证。当用户请求需要授权(例如角色)时,spring sec cas集成(过滤器)通过将其转发以登录到CAS服务器来开始进行身份验证,然后将其重定向到其原始请求。很好。

现在,我们需要组织外部的用户才能登录(没有CAS帐户)。

我的当前配置有两个问题:

1)它会将所有未经身份验证的用户转发到CAS服务。相反,我想将它们转发到/ login的应用内登录表单(使用POST方法login);这提供了使用CAS登录的选项。 See redacted screenshot of sign in form

2)当用户使用/ login表单并单击“使用CAS登录”时,我希望他们通过CAS进行身份验证,然后让他们继续其原始请求。

两个AuthenticationProvider都是分段工作的。 dao身份验证提供程序只有在访问/login时才能访问。否则会发生上述问题1。我不知道如何在他们单击按钮时配置#2(/login/cas上的GET?但是当GET被视为新请求却又被放弃时,如何将它们返回到原始请求?)


一种总结预期行为的方案:未经身份验证的用户向受保护区域发出Web请求。用户被转发到/ login

a)他们使用电子邮件和密码(已通过daoAutheticationProvider验证)登录。或者

b)他们单击使用CAS按钮登录(已通过casAuthenticationProvider验证)。

通过身份验证后,它们将转发回其原始请求。


这里是代码,全部在java配置中,我们在方法上使用@PreAuthorize,而不在HttpSecurity配置上使用antmatchers,但如果解决方案需要如何工作,愿意返回到antmatchers

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationmanagerBuilder auth) {
    auth.authenticationProvider(daoAuthenticationProvider());
    auth.authenticationProvider(casAuthenticationProvider());
}

private DaoAuthenticationProvider daoAuthenticationProvider() {
    DaoAuthenticationProvider ap = new DaoAuthenticationProvider();
    ap.setUserDetailsService(daoUserDetailsService); //implements UserDetailsService
    return ap;
}

private CasAuthenticationProvider casAuthenticationProvider() {
    CasAuthenticationProvider ap = new CasAuthenticationProvider();
    ap.setauthenticationUserDetailsService(casCustomUserDetailsService); //extends AbstractCasAssertionUserDetailsService
    ap.setServiceProperties(serviceProperties());
    ap.setTicketValidator(new Cas30ServiceTicketValidator(casServiceUrl));
    ap.setKey("obviouslyredacted");
    return ap;
}

private CasAuthenticationFilter casAuthenticationFilter() throws Exception {
    CasAuthenticationFilter filter = new CasAuthenticationFilter();
    filter.setfilterProcessesUrl("/j_spring_cas_security_check");
    filter.setauthenticationmanager(authenticationmanager());
    return filter;
}

private LogoutFilter requestSingleLogoutFilter() {
    LogoutFilter filter = new LogoutFilter(casServiceUrl + "/logout",new SecurityContextLogoutHandler());
    filter.setfilterProcessesUrl("/j_spring_cas_security_logout");
    return filter;
}

private SingleSignOutFilter singleSignOutFilter() {
    SingleSignOutFilter filter = new SingleSignOutFilter();
    filter.setCasServerUrlPrefix(casServiceUrl);
    filter.setIgnoreInitConfiguration(true);
    return filter;
}

private CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
    CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
    ep.setLoginUrl(casServiceUrl + "/login");
    ep.setServiceProperties(serviceProperties());
    return ep;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    XFrameOptionsHeaderWriter xframeoptions = new XFrameOptionsHeaderWriter(SAMEORIGIN);
    XXssprotectionHeaderWriter xxssprotection = new XXssprotectionHeaderWriter();
    xxssprotection.setEnabled(true);

    http
            .sessionmanagement()
            .sessionCreationPolicy(ALWAYS)

            .and().csrf().disable()
            .authorizeRequests()
            .antMatchers("/login").permitAll()
            .antMatchers("/css/**").permitAll()
            .antMatchers("/favicon_192x192.png").permitAll()
            .anyRequest().authenticated()

            .and().formLogin()
            .loginPage("/login")
            .permitAll()

            .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/dologout"))
            .invalidateHttpSession(true)
            .deleteCookies("JSESSIONID")
            .permitAll()

            .and().exceptionHandling()
            .accessDeniedPage(NAV_URL_ERROR + "/403")

            .and().headers().addHeaderWriter(xframeoptions)
            .and().headers().addHeaderWriter(xxssprotection);

    //Only enable CAS in Production or SPI
    if (activeProfile.equals(ENV_PRODUCTION) || activeProfile.equals(ENV_SPI))
        http
                .addFilter(casAuthenticationFilter())
                .addFilterBefore(requestSingleLogoutFilter(),LogoutFilter.class)
                .addFilterBefore(singleSignOutFilter(),CasAuthenticationFilter.class)
                .logout()
                .logoutUrl("/dologout")
                .logoutSuccessUrl(applicationBaseUrl + "/j_spring_cas_security_logout")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .permitAll()

                .and()
                .exceptionHandling()
                .authenticationEntryPoint(casAuthenticationEntryPoint())

                .and()
                .csrf().disable()
                .headers().frameOptions().disable();
}

}

也感觉我的http config方法可以清理(此项目已从启动1.x天迁移过来)

更新: 有了Marco的评论,我得以找到解决方案。

我创建了一个扩展CasAuthenticationPoint的自定义类,该自定义类在所有重写的方法上调用super(...),只是公开了最终的和受保护的方法进行操作(我仍然需要CAS来创建和编码服务并进行重定向网址)。

private CommonAuthenticationEntryPoint commonAuthenticationEntryPoint() {
        CustomCasAuthenticationEntryPoint ep = new CustomCasAuthenticationEntryPoint();
        ep.setLoginUrl(casServiceUrl + "/login");
        ep.setServiceProperties(serviceProperties());
        return new CommonAuthenticationEntryPoint(ep);
    }

然后是AutheticationEntryPoint:

public class CommonAuthenticationEntryPoint implements AuthenticationEntryPoint,InitializingBean {

    private final CustomCasAuthenticationEntryPoint customCasAuthenticationEntryPoint;

    public CommonAuthenticationEntryPoint(CustomCasAuthenticationEntryPoint customCasAuthenticationEntryPoint) {
        this.customCasAuthenticationEntryPoint = customCasAuthenticationEntryPoint;
    }

    @Override
    public void afterPropertiesSet() {
        customCasAuthenticationEntryPoint.afterPropertiesSet();
    }

    @Override
    public void commence(final HttpServletRequest request,final HttpServletResponse response,final AuthenticationException authenticationException) throws IOException {
        if (request.getParameter("cas") == null)
            response.sendRedirect("login");
        else {
            final String urlEncodedService = customCasAuthenticationEntryPoint.createServiceUrl(request,response);
            final String redirectUrl = customCasAuthenticationEntryPoint.createRedirectUrl(urlEncodedService);

            customCasAuthenticationEntryPoint.preCommence(request,response);

            response.sendRedirect(redirectUrl);
        }
    }
}

百里香叶中的按钮可以做到这一点:

<a class="btn btn-lg btn-default btn-block" th:href="@{${session.get('SPRING_SECURITY_SAVED_REQUEST').redirectUrl}(cas=true)}">
    <img th:src="@{/favicon_192x192.png}" aria-hidden="true" alt="" id="org-sso"/> Organization CAS
</a>
gzblue_zhuzh 回答:从单个登录表单入口点启动Spring多个身份验证提供程序

CAS旨在用作中央服务,因此设置此流程有点奇怪。为了使CAS能够集成所需的所有身份验证提供程序,最好始终使用CAS登录页面,并让它对照提供程序验证用户凭据:如果您允许“外部”用户访问,也可以使用CAS配置的身份验证提供程序。您尝试过这种方式吗?

本文链接:https://www.f2er.com/2938580.html

大家都在问