Spring Security 实战 - Remember me

作者: 紫霞等了至尊宝五百年 | 来源:发表于2018-11-21 20:03 被阅读46次

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

    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

    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

    • .判断是否勾选记住我


       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

    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

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

    相关文章

      网友评论

      本文标题:Spring Security 实战 - Remember me

      本文链接:https://www.haomeiwen.com/subject/fficqqtx.html