zoukankan      html  css  js  c++  java
  • springsecurity remember-me 功能

      本文基于spring-security-web-4.1.2.RELEASE。

    要实现rememberMe,有两种方案。

    1.基于简单加密token的方法

    首先需要在配置文件中加入<remember-me />,然后在登录页表单中加入复选框即可。

    <input type="checkbox" name="remember-me" value="true" checked="checked"/>两周之内不必登陆

    分析:

    这种方式实现方式是在当用户选择了记住我成功登录后,Spring Security会判断request中是否包含remember-me参数,判断逻辑在spring-security-web包的

      org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices中,如下:

            String paramValue = request.getParameter(parameter);
    
            if (paramValue != null) {
                if (paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")
                        || paramValue.equalsIgnoreCase("yes") || paramValue.equals("1")) {
                    return true;
                }
            }

    然后服务器将会生成一个token放入cookie(该cookie默认名称remember-me)中。token值由如下方式组成:

    base64(username+":"+expirationTime+":"+md5Hex(username+":"+expirationTime+":"+password+":"+key))

    • username:登录的用户名。
    • password:登录的密码。
    • expirationTime:token失效的日期和时间,以毫秒表示。
    • key:用来防止修改token的一个key。

    生成cookie的逻辑在org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices中。源码如下:

    String username = retrieveUserName(successfulAuthentication);
            String password = retrievePassword(successfulAuthentication);
    
            // If unable to find a username and password, just abort as
            // TokenBasedRememberMeServices is
            // unable to construct a valid token in this case.
            if (!StringUtils.hasLength(username)) {
                logger.debug("Unable to retrieve username");
                return;
            }
    
            if (!StringUtils.hasLength(password)) {
                UserDetails user = getUserDetailsService().loadUserByUsername(username);
                password = user.getPassword();
    
                if (!StringUtils.hasLength(password)) {
                    logger.debug("Unable to obtain password for user: " + username);
                    return;
                }
            }
    
            int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
            long expiryTime = System.currentTimeMillis();
            // SEC-949
            expiryTime += 1000L * (tokenLifetime < 0 ? TWO_WEEKS_S : tokenLifetime);
          //生成加密后的token
            String signatureValue = makeTokenSignature(expiryTime, username, password);
    
            setCookie(new String[] { username, Long.toString(expiryTime), signatureValue },
                    tokenLifetime, request, response);

    以后客户端再次访问受限资源时,spring-security解码名为remember-me的cookie,获得有效期限和username,判断后自动在系统中认证,从而实现免登录。

    这样做是存在安全隐患的,那就是在用户获取到实现记住我功能的cookie后,任何用户都可以在该cookie过期之前通过该cookie进行自动登录,即是说无法防范cookie被盗用后带来的风险。

    如果希望我们的应用能够更安全一点,可以使用接下来要介绍的持久化token方式,或者不使用Remember-Me功能,因为Remember-Me功能总是有点不安全的。

    2.基于persistent(持久化)token的方法

    最简单的实现方式如下:

    a.配置文件:

       
    <!-- token有两种持久化方案,一种基于内存(InMemoryTokenRepositoryImpl),一种基于数据库(JdbcTokenRepositoryImpl)-->
    <!-- 这里我们选择基于数据库的方式,需要注入dataSource(请自行配置DataSource)-->
    
      <remember-me data-source-ref="dataSource"/>
    

    b.数据库中插入表 persistent_logins,插入表的语句是 org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl 提供的,其中还定义了固定的sql语句来查询token。请自行阅读源码。

     create table persistent_logins (username varchar(64) not null default '', series varchar(64) primary key, token varchar(64) not null , last_used timestamp not null)

    c.在登录页表单中加入复选框

    <input type="checkbox" name="remember-me" value="true" checked="checked"/>两周之内不必登陆

    这样就完成了持久化token的设置。

    分析:

    基于持久化token的方法采用这样的实现逻辑:

           (1)用户选择了“记住我”成功登录后,将会把username、随机产生的序列号、生成的token存入一个数据库表中,同时将它们的组合生成一个cookie发送给客户端浏览器。

           (2)当下一次没有登录的用户访问系统时,首先检查cookie,如果对应cookie中包含的username、序列号和token与数据库中保存的一致,则表示其通过验证,系统将重新生成一个新的token替换数据库中对应组合的旧token,序列号保持不变,同时删除旧的cookie,重新生成包含新生成的token,就的序列号和username的cookie发送给客户端。

           (3)如果检查cookie时,cookie中包含的username和序列号跟数据库中保存的匹配,但是token不匹配。这种情况极有可能是因为你的cookie被人盗用了,由于盗用者使用你原本通过认证的cookie进行登录了导致旧的token失效,而产生了新的token。这个时候Spring Security就可以发现cookie被盗用的情况,它将删除数据库中与当前用户相关的所有token记录,这样盗用者使用原有的cookie将不能再登录,同时提醒用户其帐号有被盗用的可能性。

           (4)如果对应cookie不存在,或者包含的username和序列号与数据库中保存的不一致,那么将会引导用户到登录页面。

    从以上逻辑我们可以看出持久化token的方法比简单加密token的方法更安全,因为一旦你的cookie被人盗用了,你只要再利用原有的cookie试图自动登录一次,那么该用户相关token将会失效,同时用户可以发现自己的cookie有被盗用的可能性。

    另外,可以通过复写 类org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl 并配置<remember-me token-repository-ref=''/>来应用自定义的数据库表,相信看过源码的同学们都做得到。

  • 相关阅读:
    PowerDesigner导出SQL脚本
    【Android进阶学习】shape和selector的结合使用(转)
    国内最新安卓渠道列表42个(转)
    安卓新框架
    通知和消息有什么区别?(转)
    XSS之xssprotect(转)
    为你的Android App实现自签名的 SSL 证书(转)
    HTTPS和HTTP的区别(转)
    IT软件技术人员的职位路线(从程序员到技术总监)
    听大神说:https和http有何区别?(转)
  • 原文地址:https://www.cnblogs.com/1xin1yi/p/7389160.html
Copyright © 2011-2022 走看看