顾乔芝士网

持续更新的前后端开发技术栈

Spring Cloud实战 | 第九篇:token失效后,refresh_token刷新

Spring Cloud实战 | 第九篇:token失效后,refresh_token刷新

一、解决措施

请求时返回access_token过期的异常时,浏览器发出一次使用refresh_token换取access_token的请求,获取到新的access_token之后,重试因access_token过期而失败的请求。

二、根据 gitee的2021-12-17- 2021-12-21修改的内容完善代码

以下摘出重要部分

1、修改ljf-auth的AuthorizationServerConfig代码

**
* @Auther: lijinfeng
* @Date: 2021/12/8
* @Description 描述:认证服务配置
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private ClientDetailsServiceImpl clientDetailsService;

    // 认证管理器 WebSecurityConfig 中创建bean
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    /**
     * 客户端信息配置:client存储方式
     */
    @Override
    @SneakyThrows
    public void configure(ClientDetailsServiceConfigurer clients) {
        System.out.println("ljf-auth:AuthorizationServerConfig::configure::OAuth2客户端【数据库加载】");
        clients.withClientDetails(clientDetailsService);
    }
    /**
     * 对应于配置AuthorizationServer安全认证的相关信息,创建ClientCredentialsTokenEndpointFilter核心过滤器
     *
     * @param security 定义令牌端点上的安全约束。配置token获取合验证时的策略
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                // 允许所有人请求令牌
                .tokenKeyAccess("permitAll()")
                // 已验证的客户端才能请求check_token端点
                .checkTokenAccess("isAuthenticated()")
                // 允许表单登录
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置授权(authorization)
     * 以及令牌(token)的访问端点和令牌服务(token services)
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        System.out.println("ljf-auth:配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)");
        endpoints
                .authenticationManager(authenticationManager)
                .tokenServices(tokenService()) //将令牌增强器注入endpoints中
                .userDetailsService(userDetailsService) //用户认证
                // refresh token有两种使用方式:重复使用(true)、非重复使用(false),默认为true
                //      1 重复使用:access token过期刷新时, refresh token过期时间未改变,仍以初次生成的时间为准
                //      2 非重复使用:access token过期刷新时, refresh token过期时间延续,在refresh token有效期内刷新便永不失效达到无需再次登录的目的
                .reuseRefreshTokens(true)
        ;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    // 令牌(token)管理服务
    @Bean
    public AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices services = new DefaultTokenServices();
        // 客户端详情服务
        // 因为是向客户端颁发令牌,所以需要知道是哪一个客户端
        services.setClientDetailsService(clientDetailsService);
        // 支持刷新令牌
        services.setSupportRefreshToken(true);
        // 令牌存储策略
        services.setTokenStore(tokenStore());
        //令牌增强
        services.setTokenEnhancer(jwtAccessTokenConverter());
        // 时效在数据库中设置
        // 令牌默认有效期24小时
       // services.setAccessTokenValiditySeconds(60);
        // 刷新令牌默认有效期3天
//        services.setRefreshTokenValiditySeconds(20);
        return  services;
    }


    /**
     * 使用非对称加密算法对token签名
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        System.out.println("从classpath下的密钥库中获取密钥对(公钥+私钥)");
        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("ljf.jks"), "123456".toCharArray());
        KeyPair keyPair = factory.getKeyPair("ljf", "123456".toCharArray());
        // CustomJwtTokenConverter自定义,可以添加属性
        JwtAccessTokenConverter converter = new CustomJwtTokenConverter();
        // CustomerAccessTokenConverter自定义,防止refresh_token刷新时无法获取用户信息。
        converter.setAccessTokenConverter(new CustomerAccessTokenConverter());
        // 设置公钥
        converter.setKeyPair(keyPair);
        return converter;
    }

    /**
     * 从classpath下的密钥库中获取密钥对(公钥+私钥)
     */
    @Bean
    public KeyPair keyPair() {
        System.out.println("从classpath下的密钥库中获取密钥对(公钥+私钥)");
        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("ljf.jks"), "123456".toCharArray());
        KeyPair keyPair = factory.getKeyPair("ljf", "123456".toCharArray());
        return keyPair;
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setHideUserNotFoundExceptions(false); // 用户不存在异常抛出
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    /**
     * 密码编码器
     * 注意:想要密码编码器生效,需要authenticationProvider()改方法注入
     * 

* 委托方式,根据密码的前缀选择对应的encoder,例如:{bcypt}前缀->标识BCYPT算法加密;{noop}->标识不使用任何加密即明文的方式 * 密码判读 DaoAuthenticationProvider#additionalAuthenticationChecks * * @return */ @Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } }

2、ljf-auth添加OAuthExceptionHandler异常处理类

/**
* 异常处理
*/
@RestControllerAdvice
@Slf4j
public class OAuthExceptionHandler {

    /**
     * 用户不存在
     *
     * @param e
     * @return
     */
    @ExceptionHandler(UsernameNotFoundException.class)
    public Result handleUsernameNotFoundException(UsernameNotFoundException e) {
        return Result.error(ResultEnum.USER_NOT_EXIST);
    }
    /**
     * 用户名和密码异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(InvalidGrantException.class)
    public Result handleInvalidGrantException(InvalidGrantException e) {
        return Result.error(ResultEnum.USERNAME_OR_PASSWORD_ERROR);
    }
    /**
     * 账户异常(禁用、锁定、过期)
     *
     * @param e
     * @return
     */
    @ExceptionHandler({InternalAuthenticationServiceException.class})
    public Result handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) {
        return Result.error(ResultEnum.USER_ABNORMAL,e.getMessage());
    }
    /**
     * token无效
     *
     * @param e
     * @return
     */
    @ExceptionHandler(InvalidTokenException.class)
    public Result handleInvalidTokenException(InvalidTokenException e) {
        if (e.getMessage().contains("Invalid refresh token (expired)"))
            return Result.error(ResultEnum.REFRESH_TOKEN_EXPIRED,e.getMessage());
         return Result.error(ResultEnum.TOKEN_INVALID,e.getMessage());
    }
}

前端通过自定义返回信息,进行token刷新



控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言