• spring security自动续签功能


    自定义配置

    @Setter
    @Getter
    public class RenewProperties {
        /**
         * 是否开启token自动续签(目前只有redis实现)
         */
        private Boolean enable = false;
    
        /**
         * 白名单,配置需要自动续签的应用id(与黑名单互斥,只能配置其中一个),不配置默认所有应用都生效
         * 配置enable为true时才生效
         */
        private List<String> includeClientIds = new ArrayList<>();
    
        /**
         * 黑名单,配置不需要自动续签的应用id(与白名单互斥,只能配置其中一个)
         * 配置enable为true时才生效
         */
        private List<String> exclusiveClientIds = new ArrayList<>();
    
        /**
         * 续签时间比例,当前剩余时间小于小于过期总时长的50%则续签
         */
        private Double timeRatio = 0.5;
    }

    自定义TokenStore

    public class CustomTokenServices extends DefaultTokenServices {
        private TokenStore tokenStore;
        private TokenEnhancer accessTokenEnhancer;
        private AuthenticationManager authenticationManager;
        private boolean supportRefreshToken = false;
        private boolean reuseRefreshToken = true;
    
        /**
         * 是否登录同应用同账号互踢
         */
        private boolean isSingleLogin;
    
        public CustomTokenServices(boolean isSingleLogin) {
            this.isSingleLogin = isSingleLogin;
        }
    
        @Override
        @Transactional
        public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
            OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
            OAuth2RefreshToken refreshToken = null;
            if (existingAccessToken != null) {
                if (isSingleLogin) {
                    if (existingAccessToken.getRefreshToken() != null) {
                        tokenStore.removeRefreshToken(existingAccessToken.getRefreshToken());
                    }
                    tokenStore.removeAccessToken(existingAccessToken);
                } else if (existingAccessToken.isExpired()) {
                    if (existingAccessToken.getRefreshToken() != null) {
                        refreshToken = existingAccessToken.getRefreshToken();
                        // The token store could remove the refresh token when the
                        // access token is removed, but we want to
                        // be sure...
                        tokenStore.removeRefreshToken(refreshToken);
                    }
                    tokenStore.removeAccessToken(existingAccessToken);
                }
                else {
                    // oidc每次授权都刷新id_token
                    existingAccessToken = refreshIdToken(existingAccessToken, authentication);
                    // Re-store the access token in case the authentication has changed
                    tokenStore.storeAccessToken(existingAccessToken, authentication);
                    return existingAccessToken;
                }
            }
    
            // Only create a new refresh token if there wasn't an existing one
            // associated with an expired access token.
            // Clients might be holding existing refresh tokens, so we re-use it in
            // the case that the old access token
            // expired.
            if (refreshToken == null) {
                refreshToken = createRefreshToken(authentication);
            }
            // But the refresh token itself might need to be re-issued if it has
            // expired.
            else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
                ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
                if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
                    refreshToken = createRefreshToken(authentication);
                }
            }
    
            OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
            tokenStore.storeAccessToken(accessToken, authentication);
            // In case it was modified
            refreshToken = accessToken.getRefreshToken();
            if (refreshToken != null) {
                tokenStore.storeRefreshToken(refreshToken, authentication);
            }
            return accessToken;
    
        }
    
        /**
         * oidc每次授权都刷新id_token
         * @param token 已存在的token
         * @param authentication 认证信息
         */
        private OAuth2AccessToken refreshIdToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
            Set<String> responseTypes = authentication.getOAuth2Request().getResponseTypes();
            if (accessTokenEnhancer != null && responseTypes.contains(SecurityConstants.ID_TOKEN)) {
                return accessTokenEnhancer.enhance(token, authentication);
            }
            return token;
        }
    
        private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
            if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
                return null;
            }
            int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
            String value = UUID.randomUUID().toString();
            if (validitySeconds > 0) {
                return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis()
                        + (validitySeconds * 1000L)));
            }
            return new DefaultOAuth2RefreshToken(value);
        }
    
        private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
            DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
            int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
            if (validitySeconds > 0) {
                token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
            }
            token.setRefreshToken(refreshToken);
            token.setScope(authentication.getOAuth2Request().getScope());
    
            return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
        }
    
        @Override
        @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
        public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
                throws AuthenticationException {
            if (!supportRefreshToken) {
                throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
            }
    
            OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
            if (refreshToken == null) {
                throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
            }
    
            OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
            if (this.authenticationManager != null && !authentication.isClientOnly()) {
                AbstractAuthenticationToken userAuthentication = (AbstractAuthenticationToken)authentication.getUserAuthentication();
                Object usesrDetails = userAuthentication.getDetails();
                // The client has already been authenticated, but the user authentication might be old now, so give it a
                // chance to re-authenticate.
                Authentication user = new PreAuthenticatedAuthenticationToken(userAuthentication, "", authentication.getAuthorities());
                user = authenticationManager.authenticate(user);
                //保存账号类型
                ((PreAuthenticatedAuthenticationToken)user).setDetails(usesrDetails);
                Object details = authentication.getDetails();
                authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
                authentication.setDetails(details);
            }
            String clientId = authentication.getOAuth2Request().getClientId();
            if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
                throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
            }
    
            // clear out any access tokens already associated with the refresh
            // token.
            tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);
    
            if (isExpired(refreshToken)) {
                tokenStore.removeRefreshToken(refreshToken);
                throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
            }
    
            authentication = createRefreshedAuthentication(authentication, tokenRequest);
            if (!reuseRefreshToken) {
                tokenStore.removeRefreshToken(refreshToken);
                refreshToken = createRefreshToken(authentication);
            }
    
            OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
            tokenStore.storeAccessToken(accessToken, authentication);
            if (!reuseRefreshToken) {
                tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
            }
            return accessToken;
        }
    
        private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) {
            OAuth2Authentication narrowed;
            Set<String> scope = request.getScope();
            OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
            if (scope != null && !scope.isEmpty()) {
                Set<String> originalScope = clientAuth.getScope();
                if (originalScope == null || !originalScope.containsAll(scope)) {
                    throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope
                            + ".", originalScope);
                }
                else {
                    clientAuth = clientAuth.narrowScope(scope);
                }
            }
            narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
            return narrowed;
        }
    
        @Override
        public void setTokenStore(TokenStore tokenStore) {
            this.tokenStore = tokenStore;
            super.setTokenStore(tokenStore);
        }
    
        @Override
        public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
            this.accessTokenEnhancer = accessTokenEnhancer;
            super.setTokenEnhancer(accessTokenEnhancer);
        }
    
        @Override
        public void setAuthenticationManager(AuthenticationManager authenticationManager) {
            this.authenticationManager = authenticationManager;
            super.setAuthenticationManager(authenticationManager);
        }
    
        /**
         * Whether to support the refresh token.
         *
         * @param supportRefreshToken Whether to support the refresh token.
         */
        @Override
        public void setSupportRefreshToken(boolean supportRefreshToken) {
            this.supportRefreshToken = supportRefreshToken;
            super.setSupportRefreshToken(supportRefreshToken);
        }
    
        /**
         * Whether to reuse refresh tokens (until expired).
         *
         * @param reuseRefreshToken Whether to reuse refresh tokens (until expired).
         */
        @Override
        public void setReuseRefreshToken(boolean reuseRefreshToken) {
            this.reuseRefreshToken = reuseRefreshToken;
            super.setReuseRefreshToken(reuseRefreshToken);
        }
    }
  • 相关阅读:
    马尔科夫过程的CKS方程的推导
    科研的一些工具和想法
    读研究生后的一些想法
    读过的一些好书以后可做参考
    latex学习笔记
    机器人工程师学习要求
    《MySQL实战45讲》(8-15)笔记
    《MySQL实战45讲》(1-7)笔记
    java11运行javaFX项目
    ClickHouse入门笔记
  • 原文地址:https://www.cnblogs.com/smile361/p/15719800.html
Copyright © 2020-2023  润新知