spring-cloud-starter-oauth2 token自动续签

本文介绍了一种自定义OAuth2资源服务器的方法,通过在Spring Security框架中配置SecurityWebFilterChain来实现用户Token的有效期刷新功能。具体实现包括了自定义认证管理器CustomAuthenticationManager,该管理器会在每次用户请求时检查并更新Token的有效期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求:

希望用户的token失效不是由登录后开始计时,而是在用户超时未请求后失效(不使用refresh_token模式),也就是要在用户每次请求后去重置token的有效期。

实现方案:网关gateway实现

ResourceServerConfiguration.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.ServerAuthenticationEntryPointFailureHandler;

/**
 * 资源服务器配置
 * @Author 陈奇
 **/
@Configuration
public class ResourceServerConfiguration {
	// 网关授权相关配置 根据自身项目调整
    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private TokenStore tokenStore;
	// 网关url鉴权实现类 extends AbstractDefaultPermissionServiceImpl implements ReactiveAuthorizationManager<AuthorizationContext>
    @Autowired
    private PermissionAuthManagerAbstract permissionAuthManager;

    @RefreshScope
    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        //认证处理器
        ReactiveAuthenticationManager customAuthenticationManager = new CustomAuthenticationManager(tokenStore);
        JsonAuthenticationEntryPoint entryPoint = new JsonAuthenticationEntryPoint();
        //token转换器
        ServerBearerTokenAuthenticationConverter tokenAuthenticationConverter = new ServerBearerTokenAuthenticationConverter();
        tokenAuthenticationConverter.setAllowUriQueryParameter(true);
        //oauth2认证过滤器
        AuthenticationWebFilter oauth2Filter = new AuthenticationWebFilter(customAuthenticationManager);
        oauth2Filter.setServerAuthenticationConverter(tokenAuthenticationConverter);
        oauth2Filter.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(entryPoint));
        oauth2Filter.setAuthenticationSuccessHandler(new Oauth2AuthSuccessHandler());
        http.addFilterAt(oauth2Filter, SecurityWebFiltersOrder.AUTHENTICATION);

        ServerHttpSecurity.AuthorizeExchangeSpec authorizeExchange = http.authorizeExchange();
        if (securityProperties.getAuth().getHttpUrls().length > 0) {
            authorizeExchange.pathMatchers(securityProperties.getAuth().getHttpUrls()).authenticated();
        }
        if (securityProperties.getIgnore().getUrls().length > 0) {
            authorizeExchange.pathMatchers(securityProperties.getIgnore().getUrls()).permitAll();
        }
        authorizeExchange
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange()
                    .access(permissionAuthManager)
                .and()
                    .exceptionHandling()
                        .accessDeniedHandler(new JsonAccessDeniedHandler())
                        .authenticationEntryPoint(entryPoint)
                .and()
                    .headers()
                        .frameOptions()
                        .disable()
                .and()
                    .httpBasic().disable()
                    .csrf().disable();
        return http.build();
    }
}

CustomAuthenticationManager.java

import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import reactor.core.publisher.Mono;

import java.util.Date;

/**
 * 请求token鉴权
 * @Author 陈奇
 **/
public class CustomAuthenticationManager implements ReactiveAuthenticationManager {
	// 这个可以动态获取 
    public static final long TOKEN_RENEW_TIME = 60 * 60 * 1000L;

    private TokenStore tokenStore;

    public CustomAuthenticationManager(TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication)
                .filter(a -> a instanceof BearerTokenAuthenticationToken)
                .cast(BearerTokenAuthenticationToken.class)
                .map(BearerTokenAuthenticationToken::getToken)
                .flatMap((accessTokenValue -> {
                    OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
                    StringBuilder msg = new StringBuilder();
                    String cnColon = ":";
                    if (accessToken == null) {
                        msg.append(ConfigConstants.INVALID_ACCESS_TOKEN).append(cnColon).append(accessTokenValue);
                        return Mono.error(new InvalidTokenException(msg.toString()));
                    } else if (accessToken.isExpired()) {
                        tokenStore.removeAccessToken(accessToken);
                        msg.append(ConfigConstants.ACCESS_TOKEN_EXPIRED).append(cnColon).append(accessTokenValue);
                        return Mono.error(new InvalidTokenException(msg.toString()));
                    }

                    OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
                    if (result == null) {
                        msg.append(ConfigConstants.INVALID_ACCESS_TOKEN).append(cnColon).append(accessTokenValue);
                        return Mono.error(new InvalidTokenException(msg.toString()));
                    }
                    // 重置token有效期
                    DefaultOAuth2AccessToken oAuth2AccessToken = (DefaultOAuth2AccessToken) accessToken;
                    oAuth2AccessToken.setExpiration(new Date(System.currentTimeMillis() + TOKEN_RENEW_TIME));
                    tokenStore.storeAccessToken(accessToken, result);
                    return Mono.just(result);
                }))
                .cast(Authentication.class);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值