是否可以进行可选的kerberos认证?
我想要的是:如果客户端(浏览器)不在域名上,它将被重定向到用户名密码网页登录。否则它将做SPNEGO做Kerberos认证。
有什么办法可以解决这个问题吗?如果有,我们需要哪些配置?
是的,你可以这样做。当服务器接收到一个未经认证的请求时,它会用401("需要授权")来回复,这是一个头。WWW-Authenticate
设为 Negotiate
. 如果 Kerberos 认证失败,服务器也会发回一个 401 页面。
每当客户端认证失败时(例如,没有任何 Kerberos 凭证,或者认证失败),就会显示 401 页面内容。
所以,要解决你的问题,你要做的就是在401页面中包含登录页面。
如果你需要在服务器收到一个未认证的请求后重定向到登录页面(就像Elias Mårtenson说的那样),你需要像这样配置spring security。
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<sec:http entry-point-ref="spnegoEntryPoint" >
<sec:custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
<sec:form-login login-page="/login.xhtml" default-target-url="/index.xhtml" />
<sec:access-denied-handler error-page="/login.xhtml" />
</sec:http>
<bean id="spnegoAuthenticationProcessingFilter" class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="kerberosServiceAuthenticationProvider" /> <!-- Used with SPNEGO -->
<sec:authentication-provider ref="kerberosAuthenticationProvider"/> <!-- Used with form login -->
</sec:authentication-manager>
<bean id="kerberosAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosAuthenticationProvider">
<property name="kerberosClient">
<bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosClient">
<property name="debug" value="true"/>
</bean>
</property>
<property name="userDetailsService" ref="customUserDetailsService"/>
</bean>
<bean id="kerberosServiceAuthenticationProvider" class="ru.rfcfefa.epod.common.base.interceptor.CustomKerberosServiceAuthenticationProvider">
<property name="ticketValidator">
<bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
<property name="servicePrincipal" value="HTTP/serverName.domain.local" />
<!-- Setting keyTabLocation to a classpath resource will most likely not work in a Java EE application Server -->
<!-- See the Javadoc for more information on that -->
<property name="keyTabLocation" value="http-server.keytab" />
<property name="debug" value="true" />
</bean>
</property>
<property name="userDetailsService" ref="customUserDetailsService" />
</bean>
<bean class="org.springframework.security.extensions.kerberos.GlobalSunJaasKerberosConfig">
<property name="debug" value="true" />
<property name="krbConfLocation" value="krb5.conf"/>
</bean>
</beans>
[libdefaults]
default_realm = DOMAIN.LOCAL
default_tkt_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
default_tgs_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
permitted_enctypes = aes128-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
[realms]
DOMAIN.LOCAL = {
kdc = serverAD.domain.local
default_domain = DOMAIN.LOCAL
}
[domain_realm]
.DOMAIN.LOCAL = DOMAIN.LOCAL
如果你使用的是Apache,那么 签名 模块可以通过在 (普通的、带内的 auth) 登录页面上使用一些 JavaScript 来实现这一点, 并在登录控制器上使用第二个路径来实现 Kerberos 验证。 常规 "登录表单会测试对 Kerberos 保护的资源的访问是否成功,如果成功,则重定向浏览器自动完成登录。
在我看来,上面的做法似乎有点落后,而且在没有严格必要的情况下增加一个 JavaScript 要求是令人反对的。 我认为Elias Mårtenson的回答是正确的--尝试使用Kerberos,然后在HTTP 401响应中回到带内登录--但这是否可行取决于你的环境。
你需要提供网络地址范围,AD 的用户将使用该地址,并实现你自己的 SpnegoEntryPoint 实现,如下图。
public class CustomSpnegoEntryPoint extends SpnegoEntryPoint {
private Logger logger = //TODO add logger
private final String forwardUrl; //for example you custom login page /login
private final String ssoDomainNetworkAddress; //e.g. 192.168.0.1/16
public CustomSpnegoEntryPoint(String forwardUrl, String ssoDomainNetworkAddress) {
super(forwardUrl);
this.forwardUrl = forwardUrl;
this.ssoDomainNetworkAddress = ssoDomainNetworkAddress;
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, ServletException {
if(userBelongsToDomain(request)) {
//do sso magic and send handshake Negotiate
super.commence(request, response, ex);
} else {
//redirect to login page
RequestDispatcher dispatcher = request.getRequestDispatcher(forwardUrl);
dispatcher.forward(request, response);
}
}
private boolean userBelongsToDomain(HttpServletRequest request) {
try {
SubnetUtils.SubnetInfo subnetInfo = new SubnetUtils(ssoDomainNetworkAddress).getInfo();
return subnetInfo.isInRange(request.getRemoteAddr());
} catch (IllegalArgumentException ex) {
logger.warn("Address is invalid. Use CIDR-notation string, e.g. '192.168.0.1/16'", ex);
return false;
}
}
}
然后将其添加到WebSecurityConfig中。
http
.exceptionHandling()
.authenticationEntryPoint(new CustomSpnegoEntryPoint(
"/login",
"192.168.0.1/16")); // or from application.properties
当用户的地址来自于域,并且配置了正确的浏览器时,SSO就会工作。当请求来自公共地址(非内网)时,服务器会强制重定向到登录页面。
希望对你有所帮助。