dromara/Sa-Token · error · SaSsoException
CODE_30001
CODE_30001
Error message
无效redirect:{url} What it means
First-stage rejection in checkRedirectUrl: the redirect value fails SaFoxUtil.isUrl(url), i.e. it is not a well-formed URL at all (no scheme, malformed host). The server validates redirect targets before handing out tickets to prevent open-redirect abuse.
Source
Thrown at sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java:416
String back = url.substring(index + length);
back = SaFoxUtil.encodeUrl(back);
// 放回url中
url = url.substring(0, index + length) + back;
return url;
}
/**
* 校验重定向 url 合法性
*
* @param client 应用标识
* @param url 下放ticket的url地址
*/
public void checkRedirectUrl(String client, String url) {
// 1、是否是一个有效的url
if( ! SaFoxUtil.isUrl(url) ) {
throw new SaSsoException("无效redirect:" + url).setCode(SaSsoErrorCode.CODE_30001);
}
// 2、截取掉?后面的部分
int qIndex = url.indexOf("?");
if(qIndex != -1) {
url = url.substring(0, qIndex);
}
// 3、不允许出现@字符
if(url.contains("@")) {
// 为什么不允许出现 @ 字符呢,因为这有可能导致 redirect 参数绕过 AllowUrl 列表的校验
//
// 举个例子 配置文件:
// sa-token.sso-server.allow-url=http://sa-sso-client1.com*
//
// 开发者原意是为了允许 sa-sso-client1.com 下的所有地址都可以下放ticket
//
// 但是如果攻击者精心构建一个url:View on GitHub (pinned to ac2c7f6e94)
Solutions
- Send a fully-qualified URL as redirect, e.g. http://client.com/sso/login
- URL-encode the redirect parameter exactly once when embedding it in the auth URL
- Check proxy/gateway rewrite rules are not stripping the scheme from the query string
Example fix
// before (client)
String authUrl = server + "/sso/auth?redirect=/sso/login";
// after
String authUrl = server + "/sso/auth?redirect=" + URLEncoder.encode("http://client.com/sso/login", "UTF-8"); Defensive patterns
Strategy: validation
Validate before calling
if(!SaFoxUtil.isUrl(redirect)) {
// reject / rebuild the redirect as an absolute URL before sending to /sso/auth
} Prevention
- Always send absolute, once-encoded redirect URLs
- Test the generated auth URL by pasting it into a browser during integration
When it happens
Trigger: /sso/auth (mode=simple) is called with a redirect parameter like '/back' or '127.0.0.1/callback' — anything that is not a parseable absolute URL.
Common situations: Client builds the redirect from a relative path; reverse proxy rewrites the query and truncates the scheme; URL-encoding bugs mangle the parameter (e.g. http%3A%2F%2F decoded twice or not at all).
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/6f73ba5618835e22.
Report an issue: GitHub.