Solon v2.7.6

简单的单点登录 SSO

</> markdown

简单的单点登录,目前可以基于 solon.sessionstate.jwt 实现。假定场景是多个管理后台,使用二级域名分别为:

  • a.demo.org
  • b.demo.org
  • c.demo.org

各个管理后台,在一个导航页面上。在导航页面上的链接("/login"),用户点击后,如果是已登录跳到管理页,未登录显示登录页。

1、简单的流程设计

  • 跳到登录页。如果有发现已登录,而跳到当前用户上次打开的页面;否则显示登录界面
  • 登录页。登录成功后,在 sessin 里登记用户标识
  • 管理页。使用验证插件的登录验证注解。(实战时,用签权框架更合适)

2、方案实现

1)添加配置

#可共享域配置(可不配,默认当前服务域名;多系统共享时要配置)
server.session.state.domain: "demo.org" #用一级域名

#Jwt 密钥(使用 JwtUtils.createKey() 生成)
server.session.state.jwt.secret: "E3F9N2kRDQf55pnJPnFoo5+ylKmZQ7AXmWeOVPKbEd8=" 

2)实现登录相关控制

登录与退出控制(jwt 会通过 cookie 传,不用管细节)

@Controller
public class LoginController {
    //登录
    @Mapping("/login")
    public ModelAndView login(Context ctx){
        if(ctx.sessionAsLong("user_id", 0L) > 0){
            //跳到管理后台主页(或者打开cookie记住的最近请求页面)
            ctx.redirect("/admin/main");
            return null;
        }else{
            //显示登录界面
            return new ModelAndView("/login");
        }
    }
    
    //登录处理
    @Mapping("/login/ajax/do")
    public Result loginAjaxDo(Context ctx, String username, String password){
        if (username == "test" && password == "1234") {
            //获取登录的用户id
            long userId = 1001;
            //登录
            ctx.sessionSet("user_id", userId);
            return Result.succeed();
        }else{
            return Result.failure();
        }
    }
    
    //退出页
    @Mapping("logout")
    public void logout(Context ctx) {
        ctx.sessionClear();
    }
}

3)与验证器 solon.validation 的登录验证做结合(实战时,用签权框架更合适)

配置登录注解的检测器

@Configuration
public class Config {
    @Bean
    public LoginedChecker loginedChecker() {
        return (anno, ctx, userKeyName) -> ctx.sessionAsLong("user_id", 0L) > 0;
    }
}

再加个登录验证器出错的处理,未登录的自动跳到登录页

//可以和其它异常处理合并一个过滤器
@Component
public class ValidatorFailureFilter implements Filter {
    @Override
    public void doFilter(Context ctx, FilterChain chain) throws Throwable {
        try {
            chain.doFilter(ctx);
        } catch (ValidatorException e) {
            if(e.getAnnotation() instanceof Logined){
                ctx.redirect("/login"); //如果验证出错的是 Logined 注解,则跳到登录页上
            }else {
                ctx.render(Result.failure(e.getCode(), e.getMessage())); 
            }
        }
    }
}

4)登录验证注解在管理页的使用

@Logined //可以使用验证注解了
@Mapping("admin")
@Controller
public class AdminController extends BaseController{
    @Mapping("test")
    public String test(){
        return "OK";
    }
}