获取当前用户及扩展

获取当前用户

后端获取用户

使用 SecurityUtils.getUser() 获取当前登录用户信息

public PigxUser getUser(Authentication authentication) {
  Object principal = authentication.getPrincipal();
  if (principal instanceof PigUser) {
    return (PigxUser) principal;
  }
  return null;
}

前端获取当前用户

import { useUserInfo } from '/@/stores/userInfo';

const user = useUserInfo().userInfos.user

资源服务器获取登录用户信息

版本说明

PIGX 4.6 版本通过 OpaqueTokenIntrospector 实现资源服务器中获取用户信息

public class PigCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {

  private final OAuth2AuthorizationService authorizationService;

  @Override
  public OAuth2AuthenticatedPrincipal introspect(String token) {
    OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
    if (Objects.isNull(oldAuthorization)) {
      throw new InvalidBearerTokenException(token);
    }

    // 客户端模式默认返回
    if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(oldAuthorization.getAuthorizationGrantType())) {
      return new PigClientCredentialsOAuth2AuthenticatedPrincipal(
        oldAuthorization.getAttributes(),
        AuthorityUtils.NO_AUTHORITIES,
        oldAuthorization.getPrincipalName()
      );
    }

    Map<String, PigxUserDetailsService> userDetailsServiceMap = SpringContextHolder
      .getBeansOfType(PigxUserDetailsService.class);

    Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values().stream()
      .filter(service -> service.support(
        Objects.requireNonNull(oldAuthorization).getRegisteredClientId(),
        oldAuthorization.getAuthorizationGrantType().getValue()
      ))
      .max(Comparator.comparingInt(Ordered::getOrder));

    UserDetails userDetails = null;
    try {
      Object principal = Objects.requireNonNull(oldAuthorization).getAttributes().get(Principal.class.getName());
      UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;
      Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal();
      userDetails = optional.get().loadUserByUser((PigxUser) tokenPrincipal);
    }
    catch (UsernameNotFoundException notFoundException) {
      log.warn("用户不存在 {}", notFoundException.getLocalizedMessage());
      throw notFoundException;
    }
    catch (Exception ex) {
      log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage());
    }
    return (PigxUser) userDetails;
  }
}

实现原理

  • 获取请求携带的 token 信息,从 Redis 查询对应的客户端信息
  • 选择对应的 userDetailsService 调用 loadUserByUser 方法
  • 获取用户的最新信息,包括实时更新的权限信息
  • 返回 UserDetails 供其他服务使用

使用注意事项

接口访问限制

仅资源服务器管理的接口(带 token 请求)可获取用户信息,对外暴露的接口无法获取

以下接口通过 @Inner 注解不进行鉴权:

@Inner
@GetMapping("/info/{username}")
public R<UserInfo> info(@PathVariable("username") String username) {
  // ...
}
常见问题

使用 @Inner 注解会给 Spring Security 添加 /info/* 忽略拦截规则,导致满足此规则的路径无法进行 token 校验,SecurityUtils.getUser() 返回空

可获取的字段

默认情况下,仅能从 User 中获取以下字段:

PigxUser可获取的字段

扩展自定义字段

刷新缓存

新增字段后必须刷新 Redis 缓存:flushdb

步骤 1:扩展 PigxUser 类

PigxUser 类中添加新字段:

public class PigxUser extends User {
  @Getter
  private String newField; // 新增字段

  public PigxUser(String newField, ...) {
    super(...);
    this.newField = newField;
  }
}

步骤 2:修改 UserDetails 构造方法

PigxUserDetailsServiceImpl.getUserDetails 方法中传入新字段:

private UserDetails getUserDetails(R<UserInfo> result) {
  // newField 从 remoteUserService.info 查询返回的 UserInfo 中获取
  return new PigxUser(newField, ...);
}