AbstractRememberMeServices细节记录

2021-05-19  本文已影响0人  Children乏

AbstractRememberMeServices有两个实现类

实现类 服务端存储token 校验后更新token有效期 cookie内容
TokenBasedRememberMeServices username, expireTime,token
PersistentRememberMeToken series,tokenValue

TokenBasedRememberMeServices

cookie经Base64解码后,获得字符串数组


image.png

内容分别为username, expireTime,token

这个token是按username:tokenExpiryTime:password:key拼接后进行MD5摘要得到
其中key是RememberMeConfigurer初始化通过UUID随机生成

    /**
     * Calculates the digital signature to be put in the cookie. Default value is MD5
     * ("username:tokenExpiryTime:password:key")
     */
    protected String makeTokenSignature(long tokenExpiryTime, String username,
            String password) {
        String data = username + ":" + tokenExpiryTime + ":" + password + ":" + getKey();
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("No MD5 algorithm available!");
        }

        return new String(Hex.encode(digest.digest(data.getBytes())));
    }

token校验关键点:会再次使用username:tokenExpiryTime:password:key生成token,并与传入的token作比对
此处的password来自userDetails,确保了cookie内容的安全性,并可知若用户更改了密码,该token亦会失效

        UserDetails userDetails = getUserDetailsService().loadUserByUsername(
                cookieTokens[0]);

        Assert.notNull(userDetails, () -> "UserDetailsService " + getUserDetailsService()
                + " returned null for username " + cookieTokens[0] + ". "
                + "This is an interface contract violation");

        // Check signature of token matches remaining details.
        // Must do this after user lookup, as we need the DAO-derived password.
        // If efficiency was a major issue, just add in a UserCache implementation,
        // but recall that this method is usually only called once per HttpSession - if
        // the token is valid,
        // it will cause SecurityContextHolder population, whilst if invalid, will cause
        // the cookie to be cancelled.
        String expectedTokenSignature = makeTokenSignature(tokenExpiryTime,
                userDetails.getUsername(), userDetails.getPassword());

        if (!equals(expectedTokenSignature, cookieTokens[2])) {
            throw new InvalidCookieException("Cookie token[2] contained signature '"
                    + cookieTokens[2] + "' but expected '" + expectedTokenSignature + "'");
        }

PersistentTokenBasedRememberMeServices

cookie经Base64解码后,获得字符串数组


image.png

内容分别为series,tokenValue

series,tokenValue均为随机生成的Base64字符串,相当于随机生成一份账密

    protected String generateSeriesData() {
        byte[] newSeries = new byte[seriesLength];
        random.nextBytes(newSeries);
        return new String(Base64.getEncoder().encode(newSeries));
    }

    protected String generateTokenData() {
        byte[] newToken = new byte[tokenLength];
        random.nextBytes(newToken);
        return new String(Base64.getEncoder().encode(newToken));
    }

其中series作为key在首次生成后即稳定不变,tokenValue在每次校验后会更新,并与series一同写回cookie

    PersistentRememberMeToken newToken = new PersistentRememberMeToken(
                token.getUsername(), token.getSeries(), generateTokenData(), new Date());

        try {
            tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(),
                    newToken.getDate());
            addCookie(newToken, request, response);
        }
        catch (Exception e) {
            logger.error("Failed to update token: ", e);
            throw new RememberMeAuthenticationException(
                    "Autologin failed due to data access problem");
        }

series作为key,将整个PersistentRememberMeToken存储在tokenRepository

PersistentRememberMeToken

包含username,series,tokenValue,date
其中date含义为创建时间,用于判断有效期
自动登录时会刷新tokenValue与date

上一篇下一篇

猜你喜欢

热点阅读