我有使用x509身份验证的spring boot应用程序。我的问题是,当身份验证失败时,我得到重定向循环而不是错误屏幕。
当身份验证失败时,我从ArhivX509UserDetailsService.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);
}
};
}
}
ArhivX509UserDetailsService.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;
}
}
如果您需要更多详细信息,请询问。
答案 0 :(得分:1)
将permitAll()应用于/ error401意味着当安全过滤链完成执行时,无论当前SecurityContext中是否存在身份验证,都将正常处理请求。
X509ErrorCheckerFilter正在从过滤链中将所有未经身份验证的请求转发到/ error401,因此永远不会应用permitAll()。相反,转发的请求再次通过过滤链并且验证失败,导致循环重定向。
要解决此问题,您有几种选择。这是一对夫妇:
您可以使用SecurityConfiguration类中的web.ignoring()禁用端点的安全性。这是最简单的选择。
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/error*");
}
对于实现ErrorController的控制器中包含的请求映射,不会调用安全过滤器链。
请参阅Spring自己的BasicErrorController以供参考(source)。
这是第二个选项,因为它删除了过滤器中重定向的任何要求。相反,它允许Spring Security通过身份验证过程完成繁重的路由请求。只要安全过滤器链完成处理后,会话的SecurityContext中没有经过身份验证的Authentication,Spring安全性将返回401并返回ErrorController指定的错误页面。