apache/shenyu · error · AuthenticationException

userName is null

Error message

userName is null

What it means

Shiro authentication realm for the ShenYu dashboard rejects a JWT whose issuer (userName) claim cannot be extracted. JwtUtils.getIssuer(token) returned an empty string, meaning the token is malformed or missing the issuer claim, so authentication cannot proceed.

Solutions

  1. Re-login to the dashboard via the login endpoint to obtain a fresh, correctly-signed JWT and update the client's stored token
  2. Decode the JWT (base64 of payload) and verify it contains a non-empty issuer/userName claim
  3. Check that the client sends the token correctly: 'Authorization: Bearer <token>' with no duplicated prefix or whitespace
  4. Ensure the admin's jwt key configuration matches between token issuance and verification environments

Example fix

// before (client script)
curl -H "Authorization: ${TOKEN}" http://admin:9095/dashboard/user/list
// after
curl -H "Authorization: Bearer ${TOKEN}" http://admin:9095/dashboard/api/login first to refresh TOKEN
Defensive patterns

Strategy: try-catch

Validate before calling

boolean hasIssuer = token != null && !token.isBlank() && new String(Base64.getDecoder().decode(token.split("\\.")[1])).contains("\"iss\"");

Type guard

boolean isValidJwtShape(String t) { return t != null && t.split("\\.").length == 3 && !t.isBlank(); }

Try / catch

try { dashboardApi.call(token); } catch (AuthenticationException e) { if (e.getMessage().contains("userName is null")) { relogin(); } else { throw e; } }

Prevention

When it happens

Trigger: A request hits the admin API with an Authorization bearer token that is empty after the earlier isEmpty(token) guard passes but has no parseable issuer claim — e.g. a truncated token, a token signed by a different JWT library without the issuer claim, or a token consisting of padding/whitespace.

Common situations: Clients caching a corrupt token in localStorage; sending a placeholder token like 'null' or 'Bearer Bearer'; a dashboard version upgrade changing JWT claim names; manually crafted tokens in scripts/curl calls against the admin REST API (port 9095).

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/b917eed0dfd1b1b2. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java:102

    @Override
    protected boolean isPermitted(final Permission permission, final AuthorizationInfo info) {
        UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();
        if (Objects.nonNull(userInfo) && ADMIN_NAME.equals(userInfo.getUserName())) {
            return true;
        }
        return super.isPermitted(permission, info);
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authenticationToken) {
        String token = (String) authenticationToken.getCredentials();
        if (StringUtils.isEmpty(token)) {
            return null;
        }

        String userName = JwtUtils.getIssuer(token);
        if (StringUtils.isEmpty(userName)) {
            throw new AuthenticationException("userName is null");
        }

        DashboardUserVO dashboardUserVO = dashboardUserService.findByUserName(userName);
        if (Objects.isNull(dashboardUserVO)) {
            throw new AuthenticationException(String.format("userName(%s) can not be found.", userName));
        }
        if (!Boolean.TRUE.equals(dashboardUserVO.getEnabled())) {
            throw new AuthenticationException(String.format("user(%s) is disabled.", userName));
        }
        String clientIdFromToken = JwtUtils.getClientId(token);
        if (StringUtils.isNotEmpty(clientIdFromToken)
                && StringUtils.isNotEmpty(dashboardUserVO.getClientId())
                && !StringUtils.equals(dashboardUserVO.getClientId(), clientIdFromToken)) {
            throw new AuthenticationException("clientId is invalid or does not match");
        }

        if (!JwtUtils.verifyToken(token, jwtProperties.getSecretKey())) {
            throw new AuthenticationException("token is error.");

View on GitHub (pinned to 567142e072)