前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Security 实战 - Remember me

Spring Security 实战 - Remember me

作者头像
JavaEdge
发布2018-12-14 15:02:37
1K0
发布2018-12-14 15:02:37
举报
文章被收录于专栏:JavaEdgeJavaEdge

有个用户初访并登录了你的网站,然而第二天又来了,却必须再次登录 于是就有了“记住我”这样的功能来方便用户使用,然而有一件不言自明的事情,那就是这种认证状态的”旷日持久“早已超出了用户原本所需要的使用范围 这意味着,他们可以关闭浏览器,然后再关闭电脑,下周或者下个月,乃至更久以后再回来,只要这间隔时间不要太离谱,该网站总会知道谁是谁,并一如既往的为他们提供所有相同的功能和服务——与许久前他们离开的时候别无二致。

1 基本原理

  • 用户认证成功之后调用RemeberMeService根据用户名名生成Token由TokenRepository写到数据库,同时也将Token写入到浏览器的Cookie中
  • 重启服务之后,用户再次登入系统会由RememberMeAuthenticationFilter过滤,从Cookie中读取Token信息,与persistent_logins表匹配判断是否使用记住我功能
  • 最后由UserDetailsService查询用户信息

2 实现

2.1 建表

2.2 登陆页面添加记住我复选框

name须为remeber-me

2.3 配置 MerryyouSecurityConfig

3 效果

4 源码分析

4.1 首次登录

AbstractAuthenticationProcessingFilter#successfulAuthentication

代码语言:javascript
复制
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        ...
        // 1 将已认证过的Authentication置于SecurityContext
        SecurityContextHolder.getContext().setAuthentication(authResult);
        // 2 登录成功调用rememberMeServices
        rememberMeServices.loginSuccess(request, response, authResult);

        // Fire event
        if (this.eventPublisher != null) {
            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
                    authResult, this.getClass()));
        }

        successHandler.onAuthenticationSuccess(request, response, authResult);
    }

AbstractRememberMeServices#loginSuccess

  • .判断是否勾选记住我
代码语言:javascript
复制
   protected void onLoginSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication successfulAuthentication) {
        // 1 获取用户名
        String username = successfulAuthentication.getName();
        // 2 创建Token
        PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, generateSeriesData(), generateTokenData(), new Date());
        try {
            // 3 存 DB
            tokenRepository.createNewToken(persistentToken);
            // 4 写到浏览器的Cookie
            addCookie(persistentToken, request, response);
        }
        catch (Exception e) {
            logger.error("Failed to save persistent token ", e);
        }
    }

二次登录Remember-me

RememberMeAuthenticationFilter#doFilter

代码语言:javascript
复制
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        // 1 判断SecurityContext中有无Authentication
        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            // 2 从Cookie查询用户信息返回RememberMeAuthenticationToken
            Authentication rememberMeAuth = rememberMeServices.autoLogin(request,
                    response);

            if (rememberMeAuth != null) {
                // Attempt authenticaton via AuthenticationManager
                try {
                    // 3 如果不为空则由authenticationManager认证
                    rememberMeAuth = authenticationManager.authenticate(rememberMeAuth);

                    // Store to SecurityContextHolder
                    SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);

                    onSuccessfulAuthentication(request, response, rememberMeAuth);
......

AbstractRememberMeServices#autoLogin

代码语言:javascript
复制
public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
     // 1 获取Cookie
     String rememberMeCookie = extractRememberMeCookie(request);
     if (rememberMeCookie == null) {
         return null;
     }

     if (rememberMeCookie.length() == 0) {
         logger.debug("Cookie was empty");
         cancelCookie(request, response);
         return null;
     }

     UserDetails user = null;

     try {
         // 2 解析Cookie
         String[] cookieTokens = decodeCookie(rememberMeCookie);
         // 3 获取用户凭证
         user = processAutoLoginCookie(cookieTokens, request, response);
         // 4 检查用户凭证
         userDetailsChecker.check(user);

         logger.debug("Remember-me cookie accepted");
         // 5 返回Authentication
         return createSuccessfulAuthentication(request, user);
     }
     ...
     cancelCookie(request, response);
     return null;
 }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.11.21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 基本原理
  • 2 实现
    • 2.1 建表
      • 2.2 登陆页面添加记住我复选框
        • 2.3 配置 MerryyouSecurityConfig
          • 3 效果
          • 4 源码分析
            • 4.1 首次登录
              • AbstractRememberMeServices#loginSuccess
              • 二次登录Remember-me
                • RememberMeAuthenticationFilter#doFilter
                  • AbstractRememberMeServices#autoLogin
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档