如何在使用 LDAP 进行身份验证的项目中正确配置 Spring 安全的记住我选项?

How to correctly configure the remember me option of Spring Security in a project that use LDAP for authentication?

我是 Spring MVC 的新手,我在尝试理解如何在我的登录页面中实现 "remember me" 功能时遇到了一些问题。

所以我正在开发一个 Spring 项目,该项目使用 4.1.7.RELEASE 版本的 Spring。此 Web 应用程序使用 Spring 安全和 LDAP 来处理用户登录。

在这个项目中,我有一个名为 login.jsp 的登录页面,这个:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>
<%
    response.setHeader("Cache-Control", "no-cache");
    response.setHeader("Pragma", "no-cache");
    response.setDateHeader("Expires", -1);
%>

<!DOCTYPE html>
<html>
<head>
<!-- link href="<c:url value="resources/css/style.css" />" rel="stylesheet"-->

<link href="<c:url value="resources/css/bootstrap.css" />"
    rel="stylesheet">
<link href="<c:url value="resources/css/bootstrap-theme.css" />"
    rel="stylesheet">

<link href="<c:url value="resources/css/login.css" />" rel="stylesheet">

<title>Login Page</title>



</head>

<body onload='document.loginForm.username.focus();'>

    <!-- div id="intestazione">
        <h1 align="center">WIFI e PNSD</h1>
    </div-->

    <div class="container">
        <section id="sezioneBenvenuto">
            <h1 id="benvenuto" >Benvenuto</h1>
        </section>

        <section id="sezioneLogo" >
            <img src="resources/img/logo2.png" id="logo">
        </section>
        <section id="sezioneLogin">
            <div id="login-box">

                <c:if test="${not empty error}">
                    <div class="error">${error}</div>
                </c:if>

                <form class="form-horizontal" name='loginForm' action="<c:url value='/j_spring_security_check' />" method='POST'>
                    <div class="form-group" style="margin: 0px;" >
                        <label class="sr-only" for="exampleInputEmail3">Email address</label>
                        <input type='text' name='username' class="form-control" id="exampleInputEmail3" placeholder="Nome Utente">
                        <br>

                    </div>

                    <div class="form-group" style="margin: 0px;" >
                        <label class="sr-only" for="exampleInputPassword3">Password</label>
                        <input type='password' name='password' class="form-control" id="exampleInputPassword3" placeholder="Password">
                        <br>
                    </div>

                    <!--  <button type="submit" class="btn btn-default">Sign in</button> -->
                    <input id="ricorda" name="submit" type="submit" class="btn btn-default" value="Accedi" style="color: #0F8BB0;" />

                    <br>

                    <div style="white-space: nowrap; padding-top: 35px;">
                        <a id="linkGestioneUtenza" class="pull-left" href="${linkGestioneUtenza}">Gestione utenza</a>
                        <div class="pull-right">
                            <input style="vertical-align: top;" type="checkbox">
                            <label style="vertical-align: top; font-weight: normal; color: #0F8BB0;">Ricordami</label>
                        </div>
                    </div>

                </form>
            </div>
        </section>
    </div>
    <!--jsp:include page="footer.jsp" /-->

    <script type="text/javascript" src="webjars/bootstrap/3.2.0/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="webjars/jquery/2.1.1/jquery.min.js"></script>

</body>
</html>

因此,正如您在前面的代码片段中看到的那样,有一个复选框代表 记住我 选择:

<div class="pull-right">
    <input style="vertical-align: top;" type="checkbox">
    <label style="vertical-align: top; font-weight: normal; color: #0F8BB0;">Ricordami</label>
</div>

好的,使用之前的 login.jsp 页面,我可以毫无问题地使用我的凭据访问我的应用程序,但是,如果我选中记住我复选框,它无法工作(事实上,当我再次访问该应用程序时,我必须再次插入我的凭据)。

所以我阅读了官方文档,发现我必须通过向浏览器发送 cookie 来实现或配置此行为,并在未来的会话中检测到 cookie 并导致自动登录,如此处所述: http://docs.spring.io/spring-security/site/docs/4.1.x-SNAPSHOT/reference/html/remember-me.html

据我所知,有两种方法可以做到:

  1. 第一个使用哈希来保护基于 cookie 的令牌的安全性

  2. 第二种使用数据库或者其他持久化存储机制来 存储生成的令牌

这里是第一个疑问:这些断言究竟意味着什么?这两种方法有什么区别?

在文档中我还可以阅读:

Note that both implemementations require a UserDetailsService. If you are using an authentication provider which doesn’t use a UserDetailsService (for example, the LDAP provider) then it won’t work unless you also have a UserDetailsService bean in your application context.

好的,在这个项目中,用户身份验证是使用 LDAP 进行的。阅读文档,在我看来 UserDetailsS​​ervice 是一个接口(因此必须由自定义具体 class 实现?),它加载特定于用户的数据。

所以现在我的疑问是:这个 UserDetailsS​​ervice 究竟是做什么的?我应该在哪里声明它才能将其放入我的 应用程序上下文 ?进入Spring安全配置文件(spring-security.xml)或者在Spring配置文件(root -context.xml) ?

这些断言究竟意味着什么?

基本上记住我的功能有两件事

  1. 登录成功后,创建一个"rememeber-me"浏览器cookie 使用基于用户名、密码的令牌(可以计算令牌并 如果需要,可以保存在 db 中,也可以即时计算的哈希值 用户,密码)。
  2. 当用户尝试在没有任何会话的情况下访问受保护页面时 (session因为超时过期),然后RememberMe服务 将尝试通过反转过程来自动登录用户(获取 cookie 并检查 userDetails 哈希或检查数据库中的持久令牌 )

这两种方法有什么区别?

TokenBasedRememberMeServices -

  1. 它使用用户详细信息的散列来保护 基于 cookie 的令牌。
  2. 它包含密码的散列,这个解决方案是潜在的 如果 cookie 被捕获,则易受攻击。

PersistentTokenBasedRememberMeServices

  1. 它使用数据库或其他持久存储机制来存储 生成的令牌。
  2. 它使用用户的唯一系列标识符。这标识了 用户的初始登录并在每次用户登录时保持不变 在该持续会话期间自动登录。它也是 包含一个随机令牌,每次用户登录时都会重新生成 通过持久的记住我的功能。这种组合 随机生成的系列和令牌被持久化,使一个蛮力 强制攻击不太可能。

您的用例示例

public class LdapUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(final String userName) throws UsernameNotFoundException {
//Basically you need user details , username,password,roles you can fetch from your LdapService

        final List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        final SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
        authorities.add(authority);

return new User(userName, "password", authorities);   }

Spring 安全配置文件 (spring-security.xml)

<!-- Remember me config -->
<security:http 
     <security:remember-me services-ref="rememberMeServices" />
</security:http>
<bean id="customUserDetailsService" class="com..LdapUserDetailsService"/>
<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
    <constructor-arg index="0" value="myAppKey" />
    <constructor-arg index="1" ref="customUserDetailsService" />
 </bean>
 <bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
    <constructor-arg index="0" value="myAppKey" />
  </bean>
  <security:authentication-manager alias="authenticationManager">
     <security:authentication-provider ref="rememberMeAuthenticationProvider"/>
  </security:authentication-manager>

UserDetailService 是一个用于加载用户特定数据(例如用户名和密码等)的接口。您将实施 UserDetailService,然后编写您的自定义用户详细信息服务以提供用户特定信息。 以下是如何使用基于 UserDetailService 的身份验证的示例:-

Custom UserDetailsService example for spring 3 security

对于记住我的选项,您最好选择 cookie。将数据库用于记住我选项不是一个好主意。只需使用 cookie。