Sa-Token 单点登录第六篇:定制化登录页面

大家好,由于单篇博客长度有限,我将针对 SSO 单点登录 开启一个系列文章,此为第六篇,想完整的了解 SSO 技术的同学欢迎点开我的账号动态参考本系列历史文章。

Sa-Token 是一个轻量级 java 权限认证框架,主要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题。
Gitee 开源地址:https://gitee.com/dromara/sa-token

本篇将介绍在集成 Sa-Token SSO 模块时,如何自定义登录页面等信息。

一、何时引导用户去登录?

方案一:前端按钮跳转

前端页面准备一个[登录]按钮,当用户点击按钮时,跳转到登录接口

<a href="javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);">登录</a>
  • 1

方案二:后端拦截重定向

在后端注册全局过滤器(或拦截器、或全局异常处理),拦截需要登录后才能访问的页面资源,将未登录的访问重定向至登录接口

/**
 * Sa-Token 配置类 
 */
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
    /** 注册 [Sa-Token全局过滤器] */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                .addInclude("/**")
                .addExclude("/sso/*", "/favicon.ico")
                .setAuth(obj -> {
                    if(StpUtil.isLogin() == false) {
                        String back = SaFoxUtil.joinParam(SaHolder.getRequest().getUrl(), SpringMVCUtil.getRequest().getQueryString());
                        SaHolder.getResponse().redirect("/sso/login?back=" + SaFoxUtil.encodeUrl(back));
                        SaRouter.back();
                    }
                })
                ;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

方案三:后端拦截 + 前端跳转

首先,后端仍需要提供拦截,但是不直接引导用户重定向,而是返回未登录的提示信息

/**
 * Sa-Token 配置类 
 */
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
    /** 注册 [Sa-Token全局过滤器] */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                .addInclude("/**")
                .addExclude("/sso/*", "/favicon.ico")
                .setAuth(obj -> {
                    if(StpUtil.isLogin() == false) {
                        // 与前端约定好,code=401时代表会话未登录 
                        SaRouter.back(SaResult.ok().setCode(401));
                    }
                })
                ;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

前端接受到返回结果 code=401 时,开始跳转至登录接口

if(res.code == 401) {
    location.href = '/sso/login?back=' + encodeURIComponent(location.href);
}
  • 1
  • 2
  • 3

这种方案比较适合以 Ajax 访问的 RestAPI 接口重定向

二、如何自定义登录视图?

方式一:在demo示例中直接更改 login.html 页面代码即可

方式二:在配置中配置登录视图地址

// 配置:未登录时返回的View 
sso.setNotLoginView(() -> {
    return new ModelAndView("xxx.html");
})
  • 1
  • 2
  • 3
  • 4

三、如何自定义登录API的接口地址?

根据需求点选择解决方案:

3.1、如果只是想在 setDoLoginHandle 函数里获取除 name、pwd 以外的参数?

// 在任意代码处获取前端提交的参数 
String xxx = SaHolder.getRequest().getParam("xxx");
  • 1
  • 2

3.2、想完全自定义一个接口来接受前端登录请求?

// 直接定义一个拦截路由为 `/sso/doLogin` 的接口即可 
@RequestMapping("/sso/doLogin")
public SaResult ss(String name, String pwd) {
    System.out.println("------ 请求进入了自定义的API接口 ---------- ");
    if("sa".equals(name) && "123456".equals(pwd)) {
        StpUtil.login(10001);
        return SaResult.ok("登录成功!");
    }
    return SaResult.error("登录失败!");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.3、不想使用/sso/doLogin这个接口,想自定义一个API地址?

答:直接在前端更改点击按钮时 Ajax 的请求地址即可

四、如何自定义 API 路由

方式一:修改全局变量

在之前的章节中,我们演示了如何搭建一个SSO认证中心:

/**
 * Sa-Token-SSO Server端 Controller 
 */
@RestController
public class SsoServerController {

    // SSO-Server端:处理所有SSO相关请求 
    @RequestMapping("/sso/*")
    public Object ssoRequest() {
        return SaSsoProcessor.instance.serverDister();
    }

    // ... 其它代码

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这种写法集成简单但却不够灵活。例如认证中心地址只能是:http://{host}:{port}/sso/auth,如果我们想要自定义其API地址,应该怎么做呢?

打开SSO模块相关源码,有关 API 的设计都定义在:
ApiName.java
中,我们可以对其进行二次修改。

例如,我们可以在 Main 方法启动类或者 SSO 配置方法中修改变量值:

// 配置SSO相关参数 
@Autowired
private void configSso(SaSsoConfig sso) {
    // 自定义API地址
    SaSsoUtil.ssoTemplate.apiName.ssoAuth = "/sso/auth2";
    // ... 

    // SSO 相关配置
    sso.setXxx ... ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

启动项目,统一认证地址就被我们修改成了:http://{host}:{port}/sso/auth2

方式二:拆分路由入口

根据上述路由入口:@RequestMapping("/sso/*"),我们给它起一个合适的名字 —— 聚合式路由。

与之对应的,我们可以将其修改为拆分式路由:

/**
 * Sa-Token-SSO Server端 Controller 
 */
@RestController
public class SsoServerController {

    // SSO-Server:统一认证地址 
    @RequestMapping("/sso/auth")
    public Object ssoAuth() {
        return SaSsoProcessor.instance.ssoAuth();
    }

    // SSO-Server:RestAPI 登录接口 
    @RequestMapping("/sso/doLogin")
    public Object ssoDoLogin() {
        return SaSsoProcessor.instance.ssoDoLogin();
    }

    // SSO-Server:校验ticket 获取账号id 
    @RequestMapping("/sso/checkTicket")
    public Object ssoCheckTicket() {
        return SaSsoProcessor.instance.ssoCheckTicket();
    }

    // SSO-Server:单点注销 
    @RequestMapping("/sso/signout")
    public Object ssoSignout() {
        return SaSsoProcessor.instance.ssoSignout();
    }

    // ... 其它方法 

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

拆分式路由 与 聚合式路由 在功能上完全等价,且提供了更为细致的路由管控。

五、易错点

易错点一:搭建好 sso-serversso-client 服务后,访问返回:{"msg": "not handle"}

返回这个信息,代表你访问的路由有错误,比如说:

  • 统一认证登录地址是:http://{host}:{port}/sso/auth
  • 而你访问的却是:http://{host}:{port}/sso/auth2

地址写错了,框架就不会处理这个请求,会直接返回 {"msg": "not handle"},所有开放地址可参考:SSO 开放接口

如果仔细检查地址后没有写错,却依然返回了这个信息,那有可能是对应的接口没有打开,比如说:

  • sso-server 端的单点注销地址:http://{host}:{port}/sso/signout
  • sso-client 端的注销地址:http://{host}:{port}/sso/logout

都需要在配置文件配置:sa-token.sso.is-slo=true后,才会打开。

易错点二:参照文档搭建SSO-Client,一直提示:Ticket无效,这是怎么回事?

如果使用的是模式二,出现此异常概率最大的原因是因为 ClientServer 没有连接同一个Redis,SSO模式二中两者必须连接同一个 Redis 才可以登录成功。

你可能会问:我看配置文件明明是同一个啊?

我的建议是:排查时不要仅凭肉眼判断,分别在你的 ClientServer 启动后调用 SaManager.getSaTokenDao().set("name", "value", 100000);
随便写入一个值,看看能不能根据你的预期写进同一个Redis里,如果能的话才能证明 ClientServer 连接的Reids 是同一个,再进行下一步排查。

@SpringBootApplication
public class SaSsoServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SaSsoServerApplication.class, args);
        System.out.println("\n------ Sa-Token-SSO 统一认证中心启动成功 ");
        // 分别在 Client 与 Server 启动后调用 set 数据代码,看看能否根据预期写入同一个 reids 
        SaManager.getSaTokenDao().set("name", "value", 100000);
    }   
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如果使用的是模式三,则排查是否有重复校验 ticket 的代码,一个 ticket 码只能使用一次,多次重复使用就会提示这个。


参考资料

← Sa-Token 单点登录第五篇:前后端分离架构下的 SSO 对接步骤 Sa-Token 单点登录第四篇:跨域、跨 Redis 下的 SSO 对接步骤 →

当前博客已开放版权,所有人均可免费转载,无需联系 Sa-Token 团队获取授权。只需要在转载时保留底部 Sa-Token 官网链接 + 开源仓库链接即可。

Copyright ©2026 Sa-Token java 权限认证 | sa-token.com | 鲁ICP备18046274号-5 | 鲁公网安备37011202002956号