我将我的Web应用程序配置为通过Active Directory repo进行身份验证,它工作正常,但我总是需要以登录表单插入凭据。
应用程序客户端将是在同一域中连接到公司网络的Windows计算机。
我需要配置我的网络应用程序,以自动验证在Windows中已经过身份验证的用户。
我正在使用此配置进行Spring Security:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
user-service-ref="userDetailsService">
<security:password-encoder hash="plaintext" />
</security:authentication-provider> <!-- for DB authentication -->
<security:authentication-provider
ref="adAuthenticationProvider" />
</security:authentication-manager>
<bean id="adAuthenticationProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg name="domain" value="mydomain.it" />
<constructor-arg name="url" value="ldap://domaincontroller.mydomain.it/" />
</bean>
注意我还需要辅助身份验证提供程序来提供数据库身份验证。
我还在IE(v 9)中设置了以下选项,该选项应启用自动登录:
但它不起作用......所以我的配置有什么问题?
注意#2 我正在使用Spring v 3.2.9和Spring Security v 3.2.3
答案 0 :(得分:2)
&#39;权利&#39;方法是做Kerberos / SPNEGO。但是,服务器需要是Windows域上的受信任节点。如果您的服务器是Windows机器,那么这应该很容易。但是,如果它是一台NIX / Linux机器,那么它就可以成为真正的PITA。
这涉及诸如在Active Directory中使用SPN(服务主体名称)进行设置以及在服务器上安装一大堆东西以与A / D集成并对其进行身份验证的事情。
如果您(或您友好的Windows基础架构团队成员)对完成所有这些工作感到满意,那就去吧!但是,如果你不熟悉它,我应该警告你,当它不起作用时,诊断问题就好了。
但是有一个快速而肮脏的选项,它不会对网络上的服务器产生任何信任。事实上,整个事情可以包含在你的Spring应用程序中。这不是那么安全但非常有效的NTLM。它很容易设置。
首先,如果没有会话,您将需要一个Servlet过滤器来拦截请求并执行握手:
import java.io.IOException;
import org.apache.commons.codec.binary.Base64;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
/**
* Simple authentication filter designed to get hold of the username via NTLM SSO.
* See Spring documentation on pre-authentication filters to see how this can be used.
* </p>
* <p>
* <a href="http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#preauth">http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#preauth</a>
* </p>
*/
@Component("ntlmFilter")
public class NtlmFilter implements Filter {
private static Logger log = LoggerFactory.getLogger(NtlmFilter.class);
public static final String USERNAME_KEY = "SM_USER";
public NtlmFilter() {
log.info("Initialising the NTLM filter.");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// No initialisation tasks.
}
@Override
public void destroy() {
// No destruction tasks.
}
/**
*
*/
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (isAuthenticated(request)) {
log.debug("Session already authenticated. Proceeding down filter chain.");
setRequestHeaders(request);
proceed(req, res, chain);
} else {
log.debug("Session not yet authenticated. Attempting to login...");
login(request, response, chain);
}
}
private void proceed(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
try {
chain.doFilter(req, res);
} catch (IOException e) {
log.error("IOException processing NtlmAuthFilter Servlet filter.", e);
throw e;
} catch (ServletException e) {
log.error("ServletException processing NtlmAuthFilter Servlet filter.", e);
throw e;
}
}
/**
* If the user name has been stored in the session, then the user has been
* authenticated by the application.
*/
private boolean isAuthenticated(HttpServletRequest req) {
if (req.getSession().getAttribute(USERNAME_KEY) != null) {
return true;
} else {
return false;
}
}
public void login(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
String username = null;
String auth = req.getHeader("Authorization");
if (auth == null) {
// First phase. Return NTLM challenge headers.
res.setHeader("WWW-Authenticate", "NTLM");
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
res.setContentLength(0);
res.flushBuffer();
return;
} else if (auth.startsWith("NTLM ")) {
byte[] msg = Base64.decodeBase64(auth.substring(5));
int off = 0, length, offset;
if (msg[8] == 1) {
// Login details are not valid. Reject.
byte z = 0;
byte[] msg1 = { (byte) 'N', (byte) 'T', (byte) 'L',
(byte) 'M', (byte) 'S', (byte) 'S', (byte) 'P',
z, (byte) 2, z, z, z, z, z, z, z, (byte) 40, z,
z, z, (byte) 1, (byte) 130, z, z, z, (byte) 2,
(byte) 2, (byte) 2, z, z, z, z, z, z, z, z, z,
z, z, z };
res.setHeader(
"WWW-Authenticate",
"NTLM " + Base64.encodeBase64String(msg1));
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
res.setContentLength(0);
res.flushBuffer();
return;
} else if (msg[8] == 3) {
// Login details seem valid. Grab the username.
off = 30;
length = msg[off + 9] * 256 + msg[off + 8];
offset = msg[off + 11] * 256 + msg[off + 10];
username = new String(msg, offset, length);
username = canonicalUsername(username);
}
}
req.getSession().setAttribute(USERNAME_KEY, username);
setRequestHeaders(req);
log.info("User details now stored in session: " + username);
proceed(req, res, chain);
}
private void setRequestHeaders(HttpServletRequest req) {
req.setAttribute(USERNAME_KEY, req.getSession().getAttribute(USERNAME_KEY));
}
/**
* To avoid issues with comparing user names with differing case and spaces,
* this method strips out extraneous spaces and lower-cases it.
*/
private String canonicalUsername(String username) {
return username.replaceAll("[^a-zA-Z0-9#]", "").toLowerCase().trim();
}
}
您可能会注意到这会在请求中创建SM_USER
标头。如果确保此过滤器在RequestHeaderAuthenticationFilter
之前运行,那么您有一个很好的设置,其中标头由SSO过滤器定义,然后所有内容都传递给标准的Spring身份验证处理。这可以这样做......
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
@Profile("secure")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired(required = true)
@Qualifier("ntlmFilter")
private Filter ntlmFilter;
@Autowired(required = true)
@Qualifier("headerAuthFilter")
private Filter headerAuthFilter;
// ...
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(ntlmFilter, RequestHeaderAuthenticationFilter.class)
.anonymous().disable()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(http403ForbiddenEntryPoint());
}
@Bean(name = "headerAuthFilter")
public Filter headerAuthFilter(AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setPrincipalRequestHeader("SM_USER");
filter.setAuthenticationManager(authenticationManager);
filter.setExceptionIfHeaderMissing(false);
return filter;
}
// ...
}
答案 1 :(得分:0)
如果要进行自动身份验证,则必须使用Kerberos / SPNEGO进行LDAP身份验证。
Spring有一个用于Kerberos / SPNEGO身份验证的模块检查此blog,它解释了Kerberos / SPNEGO如何工作以及如何配置spring安全性。
此外,您必须在IE中启用“Windows Integerated Authentication”,如下所示。
答案 2 :(得分:0)