JustAuth Illegal state xx问题

排查

  1. 起因
    image.png
    服务上线生产环境后使用飞书登录有些时候会登录失败,查看日志出现以上错误Illegal state [FEISHU],但是测试环境没有出现这个情况

  2. 排查

经过排查发现是JustAuth 报的错
image.png

  1. 分析出现原因

在JustAuth找到出现原因和解决方案
原文地址:异常相关问题 | JustAuth

异常原因

  1. 单机的情况
    • state 默认有效期为3分钟(#AuthCacheConfig.java(opens new window)),第三方回调会开发者服务器后,因为异常原因在3分钟内没有处理回调请求,此时会抛出该异常。
  2. 集群的情况
    • 默认 state 是缓存到 map 中,当开发者的服务为集群时,不同集群间的内存缓存无法共享,抛出该异常。具体异常流程为:有 AB 两台服务器, A 服务器生成授权链接(同时生成 state 并存到 A 服务器的本机内存中),第三方登录完回调到了 B 服务器,此时 B 服务器内存中没有 state 缓存,并且无法访问到 A 服务器的内存。 系统抛出异常。
  3. 内网穿透的情况
    • 服务部署到云端,通过内网穿透到本机。在云端生成授权链接(同时生成 state 并存到云端服务器的内存中),回调请求穿透到本地,异常流程参考上面 “集群情况”。
  4. 前后端分离的情况
    • 项目实现前后端分离,并且生成第三方授权链接时为前端项目自行拼接,未使用 JA 提供的 authorize 方法。JA 在处理 code 换 token 时,默认会校验 state 是否存在,前端自己拼接的链接授权后,即使第三方返回了 state,但因为没有使用 JA 提供的 authorize 方法,所以缓存中并不存在 state,因此会导致验证失败。

解决方案

  1. 结合异常原因分析本次生产出现这个异常的原因
  • 我们的测试环境是单台机器所以不会出现这个问题
  • 我们这个服务在生产部署了2个
  • 所以异常原因是集群导致的state异常
  1. 解决方案
  • 快速临时解决方案: 停掉一个服务(快速解决问题,让服务可用)
  • 集群解决方案:自定义state缓存

JustAuth 自定义state缓存(redis缓存)

  1. 添加redis依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 添加redis配置
# 集成redis实现自定义的state缓存
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=xxxx
  1. 实现state缓存接口
package me.zhyd.justauth;

import me.zhyd.oauth.cache.AuthCacheConfig;
import me.zhyd.oauth.cache.AuthStateCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

/**
 * 扩展Redis版的state缓存
 *
 * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
 * @version 1.0
 * @date 2019/10/24 13:38
 * @since 1.8
 */
@Component
public class AuthStateRedisCache implements AuthStateCache {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private ValueOperations<String, String> valueOperations;

    @PostConstruct
    public void init() {
        valueOperations = redisTemplate.opsForValue();
    }

    /**
     * 存入缓存,默认3分钟
     *
     * @param key   缓存key
     * @param value 缓存内容
     */
    @Override
    public void cache(String key, String value) {
        valueOperations.set(key, value, AuthCacheConfig.timeout, TimeUnit.MILLISECONDS);
    }

    /**
     * 存入缓存
     *
     * @param key     缓存key
     * @param value   缓存内容
     * @param timeout 指定缓存过期时间(毫秒)
     */
    @Override
    public void cache(String key, String value, long timeout) {
        valueOperations.set(key, value, timeout, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取缓存内容
     *
     * @param key 缓存key
     * @return 缓存内容
     */
    @Override
    public String get(String key) {
        return valueOperations.get(key);
    }

    /**
     * 是否存在key,如果对应key的value值已过期,也返回false
     *
     * @param key 缓存key
     * @return true:存在key,并且value没过期;false:key不存在或者已过期
     */
    @Override
    public boolean containsKey(String key) {
        return redisTemplate.hasKey(key);
    }
}
  1. 获取Request(以飞书为例)
// 1. 注入新添加的cache
@Autowired
private AuthStateRedisCache stateRedisCache;

// 2. 创建request时传入stateRedisCache
AuthRequest authRequest = new AuthFeishuRequest(AuthConfig.builder()
      .clientId("clientId")
      .clientSecret("clientSecret")
      .redirectUri("redirectUri")
      .build(), stateRedisCache);// 此处传入自定义实现的类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值