单点登录不工作在Spring的web应用程序 [英] Single sign-on not working in Spring web app

查看:211
本文介绍了单点登录不工作在Spring的web应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我配置我的web应用程序通过Active Directory回购进行身份验证,它工作正常,但我总是需要插入凭证登录表单。

I configured my web application to authenticate via Active Directory repo and it works fine, but I always need to insert credentials in login form.

应用程序客户端将Windows计算机连接到公司网络都在同一个域中。

The application clients will be Windows machines connected to the company network all in the same domain.

我需要配置我的web应用程序来自动进行身份验证,已被认证在Windows用户。

I need to configure my web app to authenticate automatically the user that has been yet authenticated in Windows.

我使用这个配置为Spring Security的:

I'm using this config for 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>

注意我还需要一个二次身份验证提供者提供的DB认证。

NOTE I also need a secondary authentication provider to provide DB authentication.

我还设置IE中的下列选项(五9),应该启用自动登录:

I also set the following option in IE (v 9) that should enable automatic logon:

但它不工作......有啥错在我的配置?

But it doesn't work... so what's wrong in my configuration?

注2 我使用Spring v 3.2.9和Spring安全v 3.2.3

NOTE #2 I'm using Spring v 3.2.9 and Spring Security v 3.2.3

推荐答案

正确的方法是做的Kerberos / SPNEGO。但是,服务器需要在Windows域一个值得信赖的节点。如果你的服务器是Windows机器上,那么这应该很容易。但是,如果它是一个'NIX / Linux计算机上,那么它可以成为一个真正的皮塔饼。

The 'right' way is to do Kerberos/SPNEGO. However, the server needs to be a trusted node on your Windows domain. If your server is a Windows machine, then that should be easy enough. However, if it's a 'NIX/Linux machine, then it can be a real PITA.

这涉及到的东西,如使用SPN(服务主体名称),将其设置在Active Directory和一大堆东西安装在服务器上与A / D整合和反对验证用户身份。

This involves things such as setting it up with an SPN (Service Principal Name) in Active Directory and the installation of a whole pile of stuff on your server to integrate with A/D and authenticate users against it.

如果你(或你友好的Windows基础架构团队成员)很高兴能与做所有这一切,然后去了!但是,如果你不熟悉它,我要提醒你,当它不工作,诊断问题是地狱。

If you (or your friendly Windows infrastructure team member) are happy with doing all that, then go for it! However, if you're not familiar with it, I should warn you that when it doesn't work, diagnosing the problem is hell.

不过有一个快速和肮脏的选项,它不涉及任何信任你的服务器在网络上。其实整个事情可以在Spring应用程序被包裹起来。那是不是-十分那么安全的,但是,它-作品NTLM。这是pretty的容易成立。

However there is a quick and dirty option, which doesn't involve any trust for your server on the network. In fact the whole thing can be wrapped up in your Spring app. That's the not-quite-so-secure-but-it-works NTLM. It's pretty easy to set up.

首先,你会希望有一个Servlet过滤器拦截请求,并进行握手,如果没有会话:

First you will want a Servlet filter to intercept requests and perform the handshakes if there is no session:

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认证通过处理。这可以像这样做......

You may notice that this creates an SM_USER header in the request. If you make sure that this filter runs before a RequestHeaderAuthenticationFilter, then you have a nice setup where the header is defined by the SSO filter and then everything is passed on to standard Spring authentication processing. This can be done like so...

@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;
    }

    // ...

}

这篇关于单点登录不工作在Spring的web应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆