{"record":{"id":"b917eed0dfd1b1b2","repo":"apache/shenyu","slug":"username-is-null","errorCode":null,"errorMessage":"userName is null","messagePattern":"userName is null","errorType":"exception","errorClass":"AuthenticationException","httpStatus":401,"severity":"error","filePath":"shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java","lineNumber":102,"sourceCode":"    @Override\n    protected boolean isPermitted(final Permission permission, final AuthorizationInfo info) {\n        UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();\n        if (Objects.nonNull(userInfo) && ADMIN_NAME.equals(userInfo.getUserName())) {\n            return true;\n        }\n        return super.isPermitted(permission, info);\n    }\n\n    @Override\n    protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authenticationToken) {\n        String token = (String) authenticationToken.getCredentials();\n        if (StringUtils.isEmpty(token)) {\n            return null;\n        }\n\n        String userName = JwtUtils.getIssuer(token);\n        if (StringUtils.isEmpty(userName)) {\n            throw new AuthenticationException(\"userName is null\");\n        }\n\n        DashboardUserVO dashboardUserVO = dashboardUserService.findByUserName(userName);\n        if (Objects.isNull(dashboardUserVO)) {\n            throw new AuthenticationException(String.format(\"userName(%s) can not be found.\", userName));\n        }\n        if (!Boolean.TRUE.equals(dashboardUserVO.getEnabled())) {\n            throw new AuthenticationException(String.format(\"user(%s) is disabled.\", userName));\n        }\n        String clientIdFromToken = JwtUtils.getClientId(token);\n        if (StringUtils.isNotEmpty(clientIdFromToken)\n                && StringUtils.isNotEmpty(dashboardUserVO.getClientId())\n                && !StringUtils.equals(dashboardUserVO.getClientId(), clientIdFromToken)) {\n            throw new AuthenticationException(\"clientId is invalid or does not match\");\n        }\n\n        if (!JwtUtils.verifyToken(token, jwtProperties.getSecretKey())) {\n            throw new AuthenticationException(\"token is error.\");","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Re-login to the dashboard via the login endpoint to obtain a fresh, correctly-signed JWT and update the client's stored token","Decode the JWT (base64 of payload) and verify it contains a non-empty issuer/userName claim","Check that the client sends the token correctly: 'Authorization: Bearer <token>' with no duplicated prefix or whitespace","Ensure the admin's jwt key configuration matches between token issuance and verification environments"],"exampleFix":"// before (client script)\ncurl -H \"Authorization: ${TOKEN}\" http://admin:9095/dashboard/user/list\n// after\ncurl -H \"Authorization: Bearer ${TOKEN}\" http://admin:9095/dashboard/api/login first to refresh TOKEN","handlingStrategy":"try-catch","validationCode":"boolean hasIssuer = token != null && !token.isBlank() && new String(Base64.getDecoder().decode(token.split(\"\\\\.\")[1])).contains(\"\\\"iss\\\"\");","typeGuard":"boolean isValidJwtShape(String t) { return t != null && t.split(\"\\\\.\").length == 3 && !t.isBlank(); }","tryCatchPattern":"try { dashboardApi.call(token); } catch (AuthenticationException e) { if (e.getMessage().contains(\"userName is null\")) { relogin(); } else { throw e; } }","preventionTips":["Always re-login rather than hand-crafting JWTs for admin API calls","Send the raw token without extra 'Bearer ' duplication when the client adds it","Validate token shape (3 dot-separated segments) before storing/sending","Clear stale tokens after admin upgrades or DB resets"],"tags":["jwt","authentication","shiro","admin"],"backgroundTag":"jwt-token-expired","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}