0
点赞
收藏
分享

微信扫一扫

SpringSecurity+ oauth2实现同账号多端同时登录

阎小妍 2022-02-28 阅读 153

问题复现

  • 项目中使用了app+ pc+ 小程序,当用户同时登录app+pc时 其中一端会被挤下线。
    在这里插入图片描述

出现原因

  • 我们使用的是redis存储token,由于redis生token算法原因,多端登录返回同一个token,导致另外一端被挤下线,出现文章开头的情况。
  • 下图就是security基于redis生成token的方式
    在这里插入图片描述
    在这里插入图片描述
  • 可见:extractKey方法中使用values去生成token,多端登录参数进入values都一样
  • 所以我们只需要重写token生成规则即可。

解决方式:重写token生成规则

  • 加入登录端标识,每个端登录都需要传不一样的标识,比如:app:1,pc:2
  • 这样不同端生成token不一样不会相互影响
/**
 * @ClassName: CustomAuthenticationKeyGenerator 
 * @Description: 自定义token生成规则
 * @author: ruyi
 * @date: 2022/2/28 19:25
 */
public class CustomAuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {

    private static final String SCOPE = "scope";

    private static final String USERNAME = "username";

    private static final String DEVICE_TYPE = "device_type";

    @Override
    public String extractKey(OAuth2Authentication authentication) {
        Map<String, String> values = new LinkedHashMap<String, String>();
        OAuth2Request authorizationRequest = authentication.getOAuth2Request();
        if (!authentication.isClientOnly()) {
            values.put(USERNAME, authentication.getName());
        }
        if (authorizationRequest.getScope() != null) {
            values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
        }
        String deviceType = authorizationRequest.getRequestParameters().get(DEVICE_TYPE);
        //不同的端不同的类别
        if (CharSequenceUtil.isNotBlank(deviceType)) {
            values.put(DEVICE_TYPE, deviceType);
        }
        // 如果是多租户系统,这里要区分租户ID 条件
        return generateKey(values);
    }
}

将自定义token生成规则装配进来

 /**
 * @ClassName: CustomTokenStoreAutoConfiguration 
 * @Description:  redis token store 自动配置
 * @author: ruyi
 * @date: 2022/2/28 19:25
 */
@RequiredArgsConstructor
@Configuration(proxyBeanMethods = false)
public class CustomTokenStoreAutoConfiguration {

	private final KeyStrResolver resolver;

	private final RedisConnectionFactory connectionFactory;

	@Bean
	public TokenStore tokenStore() {
		ThgplatformRedisTokenStore tokenStore = new ThgplatformRedisTokenStore(connectionFactory, resolver);
		tokenStore.setPrefix(SecurityConstants.PIGX_PREFIX + SecurityConstants.OAUTH_PREFIX);
		tokenStore.setAuthenticationKeyGenerator(new CustomAuthenticationKeyGenerator () {
			@Override
			public String extractKey(OAuth2Authentication authentication) {
				// 增加租户隔离部分 租户ID:原生计算值
				return resolver.extract(super.extractKey(authentication), StrUtil.COLON);
			}
		});
		return tokenStore;
	}

}
  • 项目使用了一个工具类
/**
 * 字符串处理,方便其他模块解耦处理
 */
public interface KeyStrResolver {

	/**
	 * 字符串加工
	 * @param in 输入字符串
	 * @param split 分割符
	 * @return 输出字符串
	 */
	String extract(String in, String split);

	/**
	 * 字符串获取
	 * @return 模块返回字符串
	 */
	String key();

}
  • SecurityConstants.PIGX_PREFIX 与SecurityConstants.OAUTH_PREFIX是两个项目常量,可以自定义或者不加,不会影响生成。

测试一下

  • device_type =1
    在这里插入图片描述
  • device_type =2
    在这里插入图片描述
    两次不同的device_type 返回的access_token都不一样- pc端也是在线的,不受影响
    在这里插入图片描述
举报

相关推荐

0 条评论