YunaiV/ruoyi-vue-pro · error · AccessDeniedException
错误的用户类型
Error message
错误的用户类型
What it means
In TokenAuthenticationFilter.buildLoginUserByToken, after a token is validated, the token's userType is compared to the request's expected userType (admin-api=ADMIN vs app-api=MEMBER). A mismatch (e.g. a member token hitting /admin-api/*) throws AccessDeniedException('错误的用户类型'). Note: only ServiceException is swallowed (returns null -> anonymous); AccessDeniedException propagates and Spring Security turns it into a 403.
Source
Thrown at yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java:82
}
}
// 继续过滤链
chain.doFilter(request, response);
}
private LoginUser buildLoginUserByToken(String token, Integer userType) {
try {
OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
if (accessToken == null) {
return null;
}
// 用户类型不匹配,无权限
// 注意:只有 /admin-api/* 和 /app-api/* 有 userType,才需要比对用户类型
// 类似 WebSocket 的 /ws/* 连接地址,是不需要比对用户类型的
if (userType != null
&& ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
throw new AccessDeniedException("错误的用户类型");
}
// 构建登录用户
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
.setInfo(accessToken.getUserInfo()) // 额外的用户信息
.setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
.setExpiresTime(accessToken.getExpiresTime());
} catch (ServiceException serviceException) {
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
return null;
}
}
/**
* 模拟登录用户,方便日常开发调试
*
* 注意,在线上环境下,一定要关闭该功能!!!
*
* @param request 请求View on GitHub (pinned to 0418084e22)
Solutions
- Use the token whose userType matches the API surface (admin token for /admin-api/*, member token for /app-api/*).
- On the client, re-authenticate against the correct login endpoint for the needed API.
- For endpoints legitimately shared across types, omit userType enforcement (the filter only checks when userType is non-null).
- Verify the SecurityConfiguration URL-pattern -> userType mapping matches your intent.
Example fix
// before: member token used against admin endpoint GET /admin-api/system/... Authorization: Bearer <member-token> // after: use an admin token for admin-api GET /admin-api/system/... Authorization: Bearer <admin-token>
Defensive patterns
Strategy: validation
Validate before calling
OAuth2AccessTokenCheckRespDTO t = oauth2TokenApi.checkAccessToken(token);
if (userType != null && t != null && !Objects.equals(t.getUserType(), userType))
throw new AccessDeniedException("错误的用户类型"); Type guard
static boolean tokenMatchesEndpoint(OAuth2AccessTokenCheckRespDTO t, Integer expected) {
return expected == null || (t != null && Objects.equals(t.getUserType(), expected));
} Try / catch
try { filter.doFilter(req, res, chain); }
catch (AccessDeniedException e) { res.setStatus(403); res.getWriter().write("wrong user type"); } Prevention
- Use admin tokens for /admin-api/* and member tokens for /app-api/*
- Re-authenticate via the correct login endpoint when switching surfaces
- Align SecurityConfiguration URL-pattern -> userType mapping with intent
When it happens
Trigger: A user logged in via the app-api (MEMBER) calls an admin-api endpoint, or vice versa; a token issued for one user type is used against the other API surface; a misrouted request (wrong URL prefix) for the token.
Common situations: Front-end using the wrong token type for an endpoint; mobile app token used against admin console; routing/URL prefix misconfiguration.
Related errors
- 400
- LoginUser(%d) Table(%s/%s) 未返回数据权限
- AreaUtils 初始化失败
- IPUtils 初始化失败
- TenantContextHolder 不存在租户编号!可参考文档:{}
AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14).
Data as JSON: /api/errors/d22e2d929eb6437f.
Report an issue: GitHub.