qq_27430221 2023-04-19 19:51 采纳率: 0%
浏览 79
已结题

reuseRefreshTokens(false)生效了时间到了仍然强制log out

** Background:**
Client reported a issue which the user always logout by the system during working. Checked the system logic, we believe it is because the access token only valid for 3 hours. That's why the user always logout at around 12pm local time.

Current Access Token Logic:
The token involve 2 part:
Access token
Default valid time = 30mins
When access token expire, system will use refresh token to get a new access token with same valid time.
Refresh token
Default valid time = 6 hours (Wunsche's setting = 3 hours)
When refresh token expire, system will logout the user.
Expectation :

When user is actively using the system, we should extend/ refresh the Refresh Token.
Generate a new refresh token with new valid time by every api request if the original refresh token haven't expire.

项目框架:angular + springboot
当前希望解决:只要ui上有操作,refresh token 永不过期,尝试使用 api: reuseRefreshTokens(false),

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .authenticationManager(authenticationManager)
            .tokenStore(tokenStore())
            .tokenEnhancer(tokenEnhancerChain())    
            .reuseRefreshTokens(false)
            .tokenGranter(appendTokenGranter(endpoints))
            .userDetailsService(userDetailsService)
            .exceptionTranslator(new WebResponseExceptionTranslatorImpl());

debug 前端可以看见access token和refresh token 都刷新,但是一旦超过初始设定的refresh token过期时间,仍然会强制log out,log报错:

2023-04-19 19:40:46,661 [4fca686a-a242-4ade-bc4d-4ba1d93fd6c3] INFO  o.s.s.o.p.endpoint.TokenEndpoint - Handling error: InvalidTokenException, Invalid refresh token (expired): eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRlVGltZUZvcm1hdENvZGUiOiJkZC9NTU0veXl5eSBISDptbTpzcyIsImlzVGJ5TWlncmF0aW9uIjpmYWxzZSwidXNlcl9uYW1lIjoiZ2F2aW5Ad3VuIiwiZGF0ZUZvcm1hdENvZGUiOiJkZC9NTU0veXl5eSIsImxvY2FsZSI6ImVuX1VTIiwid29ya2luZ19kb21haW4iOiJXVU5TQ0hFIiwiZXhwaXJlZE9uIjoxNjgxOTA0NDE1ODAyLCJjbGllbnRfaWQiOiJjYngiLCJkb21haW5faWQiOiJXVU5TQ0hFIiwiY3VycmVudFVzZXJDb21iaW5lZElkIjoiZ2F2aW5Ad3VuQFdVTlNDSEUiLCJvd25lckRvbWFpbklkIjoiV1VOU0NIRSIsInJlZnJlc2hUb2tlbklkIjoiNTgwYjAyMTktYjI1My00YWQzLWJlZTMtNzVhMDJmMzM2YWZmIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIiwidHJ1c3QiXSwiYXRpIjoiOTM0ZWU5ZWItODlmYy00OTgzLWJlNDQtOTg1NjM0NGJhMTk1IiwibmV3dWlUaW1lWm9uZUNvZGUiOiJBZnJpY2EvQWxnaWVycyIsImV4cCI6MTY4MTkwNDQyNCwianRpIjoiNTgwYjAyMTktYjI1My00YWQzLWJlZTMtNzVhMDJmMzM2YWZmIiwiZW1haWwiOiJnYXZpbi50ZXN0QGNieHNvZnR3YXJlLmNvbSIsInJlcXVlc3RDbGllbnQiOm51bGwsImN1cnJlbnRVc2VyTmFtZSI6ImdhdmluIHd1biIsInRva2VuSWQiOiI5MzRlZTllYi04OWZjLTQ5ODMtYmU0NC05ODU2MzQ0YmExOTUiLCJ3b3JraW5nRm9yVXNlcklkIjpudWxsLCJ1c2VySWQiOiI5ZmE5ZTFhNTMxYTI0MDQwOWE1NTRiMGFiOWRlNjNkNCIsImF1dGhvcml0aWVzIjpbInVzZXIiXSwiaXNTaW5nbGVTb3VyY2luZ0RvbWFpbiI6dHJ1ZSwidXNlckxvZ2luSWQiOiJnYXZpbkB3dW4ifQ.6rMSFCuDLMkiUqx9MbgLtzuHVWnw_H6oh1wv80dYJ60
2023-04-19 19:40:46,662 [] INFO  c.c.c.c.CustomizeEmbeddedTomcatContainer - POST /oauth/token status=401, time_taken=27, bytes=1268, query_string=, request_uuid=4fca686a-a242-4ade-bc4d-4ba1d93fd6c3, remote_ip_address=127.0.0.1

以下是具体代码:

package com.cbxsoftware.rest.configuration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import com.cbxsoftware.rest.security.CbxAccessTokenConverter;
import com.cbxsoftware.rest.security.CbxTokenEnhancer;
import com.cbxsoftware.rest.security.WebResponseExceptionTranslatorImpl;
import com.cbxsoftware.rest.security.checker.AuthenticationChecker;
import com.cbxsoftware.rest.security.grant.CasTicketGranter;
import com.cbxsoftware.rest.service.DomainAttributeService;
import com.cbxsoftware.rest.service.SsoPgtService;
import lombok.RequiredArgsConstructor;
@Configuration
@RequiredArgsConstructor
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    private static final String GRANT_TYPE = "password";
    private static final String AUTHORIZATION_CODE = "authorization_code";
    private static final String REFRESH_TOKEN = "refresh_token";
    private static final String IMPLICIT = "implicit";
    private static final String SCOPE_READ = "read";
    private static final String SCOPE_WRITE = "write";
    private static final String TRUST = "trust";
    private static final String CAS = "cas";
    @Value("${cbx.client.id:cbx}")
    private String clientId;
    @Value("${cbx.client.secret:cbx@123}")
    private String clientSecret;
    @Value("${cbx.access_token.validitySeconds:300}")
    private Integer accessTokenValiditySeconds;
    @Value("${cbx.refresh_token.validitySeconds:21600}")
    private Integer refreshTokenValiditySeconds;
    @Value("${cbx.jwt.key:cbxRest@123}")
    private String jwtKey;
    private final AuthenticationManager authenticationManager;
    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;
    private final SsoPgtService ssoPgtService;
    private final DomainAttributeService domainAttributeService;
    private final List<AuthenticationChecker> authenticationCheckers;
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setAccessTokenConverter(new CbxAccessTokenConverter());
        converter.setSigningKey(jwtKey);
        return converter;
    @Bean
    public TokenEnhancerChain tokenEnhancerChain() {
        //add additional information to JWT token
        final CbxTokenEnhancer cbxTokenEnhancer = new CbxTokenEnhancer(domainAttributeService);
        final List<TokenEnhancer> tokenEnhancers = Arrays.asList(cbxTokenEnhancer, accessTokenConverter());
        final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
        return tokenEnhancerChain;
    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient(clientId)
            .secret(passwordEncoder.encode(clientSecret))
            .authorizedGrantTypes(GRANT_TYPE, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT, CAS)
            .scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
            .accessTokenValiditySeconds(accessTokenValiditySeconds)
            .refreshTokenValiditySeconds(refreshTokenValiditySeconds);
    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints)




    
 {
        endpoints
            .authenticationManager(authenticationManager)
            .tokenStore(tokenStore())
            .tokenEnhancer(tokenEnhancerChain())    
            .reuseRefreshTokens(false)
            .tokenGranter(appendTokenGranter(endpoints))
            .userDetailsService(userDetailsService)
            .exceptionTranslator(new WebResponseExceptionTranslatorImpl());
    private TokenGranter appendTokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
        final List<TokenGranter> granters = Collections.singletonList(endpoints.getTokenGranter());
        final CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granters);
        compositeTokenGranter.addTokenGranter(retrieveCasTicketGranter(endpoints));
        return compositeTokenGranter;
    private TokenGranter retrieveCasTicketGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
        return new CasTicketGranter(
            endpoints.getTokenServices(),
            endpoints.getClientDetailsService(),
            endpoints.getOAuth2RequestFactory(),
            userDetailsService,
            ssoPgtService,
            authenticationCheckers

angular 前端拦截器代码:

```bash import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { first, switchMap } from 'rxjs/operators'; import { AuthService } from '../auth.service'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private readonly authService: AuthService) {} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (isRefreshTokenRequest(req)) { return next.handle(req); return this.authService.userInfo$.pipe( first(), switchMap((userInfo) => { if (userInfo.access_token) { // Clone the request and set the new header in one step. const authReq = req.clone({ setHeaders: { Authorization: `${userInfo.token_type} ${userInfo.access_token}`, // send cloned request with header to the next handler. return next.handle(authReq); } else { return next.handle(req); function isRefreshTokenRequest(req: HttpRequest<any>) { return typeof req.body === 'string' && req.body.includes('grant_type=refresh_token');
  • 编辑 收藏 删除
  • 追加酬金 (90%的用户在追加酬金后获得了解决方案)

    当前问题酬金

    ¥ 50 (您已提供 ¥ 20, 还可追加 ¥

    支付方式

    余额支付

    余额: ¥ 499

    扫码支付

    提供问题酬金的用户不参与问题酬金结算和分配

    支付即为同意 《付费问题酬金结算规则》

    5 条回答 默认 最新

    查看更多回答(-1条) 报告相同问题?

    问题事件

    • 系统已结题 4月27日
    • 修改了问题 4月19日
    • 修改了问题 4月19日
    • 创建了问题 4月19日

    悬赏问题

    • ¥15 python selenium爬虫input隐藏属性的值时打印不出来 C++把数据保存在网络上 我想用专用ip设置路由器 C++问题,具体问题看截图 C++问题:积木问题 C++问题:魔法王国中有一棵存活千年的古树,由于长时间缺水,它的生命已经岌岌可危。 C++实现玛雅字典问题 关于#c语言#的问题:输出树中任意两个指定节点之间的路径