x509身份验证失败时重定向循环

时间:2017-09-05 13:32:00

标签: java spring spring-boot spring-security x509

我有使用x509身份验证的spring boot应用程序。我的问题是,当身份验证失败时,我得到重定向循环而不是错误屏幕。

当身份验证失败时,我从ArhivX509UserDetailsS​​ervice.java中的方法抛出UsernameNotFoundException loadUserDetails

我的代码如下:

SecurityConfiguration.java

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
@Qualifier("x509UserDetailsService")
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> iX509UserDetailsService;

@Override
protected void configure(HttpSecurity pHttp) throws Exception {
    //@formatter:off
    pHttp
        .authorizeRequests()
            .antMatchers("/webjars/**").permitAll()
            .antMatchers("/error").permitAll()
            .antMatchers("/error401").permitAll()
            .anyRequest().authenticated()
            .and()
        .x509()
            .subjectPrincipalRegex("(.*)")
            .authenticationUserDetailsService(iX509UserDetailsService)
            .and()
            .addFilterAfter(new X509ErrorCheckerFilter(),  X509AuthenticationFilter.class)
            .addFilterBefore(new LoggerMDCFilter(),  X509ErrorCheckerFilter.class)
        .exceptionHandling()
            .authenticationEntryPoint(unauthorizedEntryPoint())
            .accessDeniedPage(AppUrls.ERROR_401)
            .and()
        .requiresChannel()
            .anyRequest()
            .requiresSecure()
            .and()
        .sessionManagement()
            .maximumSessions(1)
            .and()
            .and()
        .logout()
            .invalidateHttpSession(true)
            .deleteCookies("SESSION", "JSESSIONID")
            .logoutSuccessUrl("http://www.google.com")
            .permitAll();
    //@formatter:on
}

@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
    return new AuthenticationEntryPoint() {
        @Override
        public void commence(HttpServletRequest pRequest, HttpServletResponse pResponse,
                AuthenticationException pAuthException) throws IOException, ServletException {
            pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        }
    };
}
}

ArhivX509UserDetailsS​​ervice.java

@Service("x509UserDetailsService")
public class ArhivX509UserDetailsService
        implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
    @Autowired
    private IUserProfileService iUserProfileService;

    @Autowired
    private ICheckCertificateService iCheckCertService;


    @Override
    public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken pToken) throws UsernameNotFoundException {
        X509Certificate tCertificate = (X509Certificate) pToken.getCredentials();
        String tSubjectDN = tCertificate.getSubjectDN().toString().toUpperCase();
        ProfilKorisnika tProfilKorisnika = iUserProfileService.getUserProfile(tSubjectDN);

        if (tProfilKorisnika == null) {
            throw new UsernameNotFoundException("Pogreška kod prijave korisnika.");
        }

        return tProfilKorisnika;

    }
}

X509ErrorCheckerFilter.java

public class X509ErrorCheckerFilter extends GenericFilterBean {
    private static final String DEFAULT_REDIRECT_URL = "/error401";

    private static final String[] UNAUTHENTICATED_URLS = { "/webjars/**", "/error", "/error401", "/login",
            "/logout" };

    @Override
    public void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
            throws IOException, ServletException {

        HttpServletRequest tHttpRequest = (HttpServletRequest) pRequest;
        HttpServletResponse tHttpResponse = (HttpServletResponse) pResponse;

        String tContextRoot = tHttpRequest.getSession().getServletContext().getContextPath();
        String tUri = tHttpRequest.getRequestURI().replaceFirst(tContextRoot, "");

        if (isUriSecured(tUri)) {
            Authentication tAuthentication = SecurityContextHolder.getContext().getAuthentication();
            AuthenticationException tException = (AuthenticationException) tHttpRequest
                    .getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);

            if (tException != null || tAuthentication == null) {
                RequestDispatcher tRd = tHttpRequest.getRequestDispatcher(DEFAULT_REDIRECT_URL);
                tRd.forward(tHttpRequest, tHttpResponse);
                return;
            }
        }

        pChain.doFilter(pRequest, pResponse);

    }

    private boolean isUriSecured(String pRequestURI) {
        boolean tResult = true;
        for (String tUrl : UNAUTHENTICATED_URLS) {
            if (pRequestURI.startsWith(tUrl)) {
                tResult = false;
                break;
            }
        }
        return tResult;
    }

}

如果您需要更多详细信息,请询问。

1 个答案:

答案 0 :(得分:1)

将permitAll()应用于/ error401意味着当安全过滤链完成执行时,无论当前SecurityContext中是否存在身份验证,都将正常处理请求。

X509ErrorCheckerFilter正在从过滤链中将所有未经身份验证的请求转发到/ error401,因此永远不会应用permitAll()。相反,转发的请求再次通过过滤链并且验证失败,导致循环重定向。

要解决此问题,您有几种选择。这是一对夫妇:

1.禁用错误端点的安全性

您可以使用SecurityConfiguration类中的web.ignoring()禁用端点的安全性。这是最简单的选择。

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/error*");
}

2。实现ErrorController接口

对于实现ErrorController的控制器中包含的请求映射,不会调用安全过滤器链。

请参阅Spring自己的BasicErrorController以供参考(source)。

这是第二个选项,因为它删除了过滤器中重定向的任何要求。相反,它允许Spring Security通过身份验证过程完成繁重的路由请求。只要安全过滤器链完成处理后,会话的SecurityContext中没有经过身份验证的Authentication,Spring安全性将返回401并返回ErrorController指定的错误页面。