dromara/Sa-Token · error · SaOAuth2ClientModelException
30114
30114
Error message
非法 redirect_url:
What it means
Thrown when the (query-stripped) redirect_uri is not present in the client's configured allowRedirectUris list (code 30114). sa-token matches the URL against the client's registered allow-list before issuing a code; exact or wildcard-suffix entries match, anything else is rejected as '非法 redirect_url'.
Source
Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java:195
// http://sa-token.com/?code=i8vDfbpqBViMe01QoLY1kHROJWYvv9plBtvTZ6kk77KK0e0U4Xj99NPfSZEYjRul
//
// 造成了 code 参数劫持
// 所以此处需要禁止在 url 中出现 @ 字符
//
// 这么一刀切的做法,可能会导致一些特殊的正常url也无法通过校验,例如:
// http://sa-oauth-server.com:8000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=http://sa-oauth-client.com/@getInfo
//
// 但是为了安全起见,这么做还是有必要的
throw new SaOAuth2ClientModelException("无效 redirect_url(不允许出现@字符):" + url)
.setClientId(clientId)
.setCode(SaOAuth2ErrorCode.CODE_30113);
}
// 4、是否在[允许地址列表]之中
SaClientModel clientModel = checkClientModel(clientId);
checkRedirectUriListNormal(clientModel.allowRedirectUris);
if( ! SaStrategy.instance.hasElement.apply(clientModel.allowRedirectUris, url)) {
throw new SaOAuth2ClientModelException("非法 redirect_url: " + url)
.setClientId(clientId)
.setCode(SaOAuth2ErrorCode.CODE_30114);
}
}
/**
* 校验配置的 allowRedirectUris 是否合规,如果不合规则抛出异常
* @param redirectUriList 待校验的 allow-url 地址列表
*/
public void checkRedirectUriListNormal(List<String> redirectUriList){
checkRedirectUriListNormalStaticMethod(redirectUriList);
}
/**
* 校验配置的 allowRedirectUris 是否合规,如果不合规则抛出异常,静态方法内部实现
* @param redirectUriList 待校验的 allow-url 地址列表
*/
public static void checkRedirectUriListNormalStaticMethod(List<String> redirectUriList){View on GitHub (pinned to ac2c7f6e94)
Solutions
- Add the exact redirect_uri to the client's allow-url configuration (sa-token style: putAllowUrl or config in the client register list)
- Fix scheme/port/path mismatches in the redirect_uri so it matches an existing allow entry
- Use a trailing '*' wildcard entry (e.g. http://client.com/*) only at the end, to cover multiple callback paths
Example fix
// before
new SaClientModel().setClientId("1001").setAllowUrl("http://sa-oauth-client.com/callback");
// redirect_uri=http://sa-oauth-client.com:8000/callback -> rejected
// after
new SaClientModel().setClientId("1001").setAllowUrl("http://sa-oauth-client.com/callback", "http://sa-oauth-client.com:8000/callback"); Defensive patterns
Strategy: validation
Validate before calling
String stripped = url.split("\\?")[0];
boolean allowed = SaStrategy.instance.hasElement.apply(client.getAllowRedirectUris(), stripped);
if (!allowed) throw new IllegalArgumentException("redirect_uri not in allow list: " + stripped); Try / catch
try { saOAuth2Template.checkRedirectUri(clientId, url); } catch (SaOAuth2ClientModelException e) { if (e.getCode() == 30114) return badRequest("redirect_uri not registered"); throw e; } Prevention
- Keep allow-url config in version control and update it in the same change as domain/port migrations
- Log the exact rejected URL (query-stripped) to make config diffs obvious
When it happens
Trigger: Calling /oauth2/authorize with a redirect_uri whose scheme, host, port, or path differs from every entry in the client's allowRedirectUris (e.g. http vs https, missing port :8000, different path, trailing slash mismatch).
Common situations: Client app moved to a new domain or port but allow-url config was not updated; http/https mismatch behind a TLS-terminating proxy; trailing-slash or context-path differences; wildcard entry 'http://client.com/*' not matching a different subdomain.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/e4b901197e93f840.
Report an issue: GitHub.