老板:公司系统太多,能不能实现账号互通?(二)

简介: 老板:公司系统太多,能不能实现账号互通?(二)

SSO 的底层原理 CAS

①CAS 实现单点登录流程

我们知道对于完全不同域名的系统,cookie 是无法跨域名共享的,因此 sessionId 在页面端也无法共享,因此需要实现单店登录,就需要启用一个专门用来登录的域名如(ouath.com)来提供所有系统的 sessionId。

当业务系统被打开时,借助中心授权系统进行登录,整体流程如下:

  • 当 b.com 打开时,发现自己未登陆,于是跳转到 ouath.com 去登陆
  • ouath.com 登陆页面被打开,用户输入帐户/密码登陆成功
  • ouath.com 登陆成功,种 cookie 到 ouath.com 域名下
  • 把 sessionid 放入后台 redis,存放<ticket,sesssionid>数据结构,然后页面重定向到 A 系统
  • 当 b.com 重新被打开,发现仍然是未登陆,但是有了一个 ticket 值
  • 当 b.com 用 ticket 值,到 redis 里查到 sessionid,并做 session 同步,然后种 cookie 给自己,页面原地重定向
  • 当 b.com 打开自己页面,此时有了 cookie,后台校验登陆状态,成功

整个交互流程图如下:

微信图片_20220908103609.png

②单点登录流程演示

CAS 登录服务 demo 核心代码如下:

用户实体类:

public class UserForm implements Serializable{
private static final long serialVersionUID = 1L;
private String username;
private String password;
private String backurl;
public String getUsername() {
    return username;
}
public void setUsername(String username) {
    this.username = username;
}
public String getPassword() {
    return password;
}
public void setPassword(String password) {
    this.password = password;
}
public String getBackurl() {
    return backurl;
}
public void setBackurl(String backurl) {
    this.backurl = backurl;
}
}

登录控制器:

@Controller
public class IndexController {
    @Autowired
    private RedisTemplate redisTemplate;
@GetMapping("/toLogin")
public String toLogin(Model model,HttpServletRequest request) {
    Object userInfo = request.getSession().getAttribute(LoginFilter.USER_INFO);
    //不为空,则是已登陆状态
    if (null != userInfo){
        String ticket = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(ticket,userInfo,2, TimeUnit.SECONDS);
        return "redirect:"+request.getParameter("url")+"?ticket="+ticket;
    }
    UserForm user = new UserForm();
    user.setUsername("laowang");
    user.setPassword("laowang");
    user.setBackurl(request.getParameter("url"));
    model.addAttribute("user", user);
    return "login";
}
@PostMapping("/login")
public void login(@ModelAttribute UserForm user,HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
    System.out.println("backurl:"+user.getBackurl());
    request.getSession().setAttribute(LoginFilter.USER_INFO,user);
    //登陆成功,创建用户信息票据
    String ticket = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set(ticket,user,20, TimeUnit.SECONDS);
    //重定向,回原url  ---a.com
    if (null == user.getBackurl() || user.getBackurl().length()==0){
        response.sendRedirect("/index");
    } else {
        response.sendRedirect(user.getBackurl()+"?ticket="+ticket);
    }
}
@GetMapping("/index")
public ModelAndView index(HttpServletRequest request) {
    ModelAndView modelAndView = new ModelAndView();
    Object user = request.getSession().getAttribute(LoginFilter.USER_INFO);
    UserForm userInfo = (UserForm) user;
    modelAndView.setViewName("index");
    modelAndView.addObject("user", userInfo);
    request.getSession().setAttribute("test","123");
    return modelAndView;
}
}

登录过滤器:

public class LoginFilter implements Filter {
    public static final String USER_INFO = "user";
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest,
                     ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
     HttpServletResponse response = (HttpServletResponse)servletResponse;
    Object userInfo = request.getSession().getAttribute(USER_INFO);;
    //如果未登陆,则拒绝请求,转向登陆页面
    String requestUrl = request.getServletPath();
    if (!"/toLogin".equals(requestUrl)//不是登陆页面
            &amp;&amp; !requestUrl.startsWith("/login")//不是去登陆
            &amp;&amp; null == userInfo) {//不是登陆状态
        request.getRequestDispatcher("/toLogin").forward(request,response);
        return ;
    }
    filterChain.doFilter(request,servletResponse);
}
@Override
public void destroy() {
}
}

配置过滤器:

@Configuration
public class LoginConfig {
//配置filter生效
@Bean
public FilterRegistrationBean sessionFilterRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new LoginFilter());
    registration.addUrlPatterns("/*");
    registration.addInitParameter("paramName", "paramValue");
    registration.setName("sessionFilter");
    registration.setOrder(1);
    return registration;
}
}

登录页面:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>enjoy login</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div text-align="center">
    <h1>请登陆</h1>
    <form action="#" th:action="@{/login}" th:object="${user}" method="post">
        <p>用户名: <input type="text" th:field="*{username}" /></p>
        <p>密  码: <input type="text" th:field="*{password}" /></p>
        <p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
        <input type="text" th:field="*{backurl}" hidden="hidden" />
    </form>
</div>
</body>
</html>
web 系统 demo 核心代码如下:

过滤器:

public class SSOFilter implements Filter {
    private RedisTemplate redisTemplate;
public static final String USER_INFO = "user";
public SSOFilter(RedisTemplate redisTemplate){
    this.redisTemplate = redisTemplate;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest,
                     ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse)servletResponse;
    Object userInfo = request.getSession().getAttribute(USER_INFO);;
    //如果未登陆,则拒绝请求,转向登陆页面
    String requestUrl = request.getServletPath();
    if (!"/toLogin".equals(requestUrl)//不是登陆页面
            &amp;&amp; !requestUrl.startsWith("/login")//不是去登陆
            &amp;&amp; null == userInfo) {//不是登陆状态
        String ticket = request.getParameter("ticket");
        //有票据,则使用票据去尝试拿取用户信息
        if (null != ticket){
            userInfo = redisTemplate.opsForValue().get(ticket);
        }
        //无法得到用户信息,则去登陆页面
        if (null == userInfo){
            response.sendRedirect("http://127.0.0.1:8080/toLogin?url="+request.getRequestURL().toString());
            return ;
        }
        /**
         * 将用户信息,加载进session中
         */
        UserForm user = (UserForm) userInfo;
        request.getSession().setAttribute(SSOFilter.USER_INFO,user);
        redisTemplate.delete(ticket);
    }
    filterChain.doFilter(request,servletResponse);
}
@Override
public void destroy() {
}
}

控制器:

@Controller
public class IndexController {
    @Autowired
    private RedisTemplate redisTemplate;
@GetMapping("/index")
public ModelAndView index(HttpServletRequest request) {
    ModelAndView modelAndView = new ModelAndView();
    Object userInfo = request.getSession().getAttribute(SSOFilter.USER_INFO);
    UserForm user = (UserForm) userInfo;
    modelAndView.setViewName("index");
    modelAndView.addObject("user", user);
    request.getSession().setAttribute("test","123");
    return modelAndView;
}
}

首页:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>enjoy index</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div th:object="${user}">
    <h1>cas-website:欢迎你"></h1>
</div>
</body>
</html>

③CAS 的单点登录和 OAuth2 的区别

OAuth2: 三方授权协议,允许用户在不提供账号密码的情况下,通过信任的应用进行授权,使其客户端可以访问权限范围内的资源。

CAS: 中央认证服务(Central Authentication Service),一个基于 Kerberos 票据方式实现 SSO 单点登录的框架,为 Web 应用系统提供一种可靠的单点登录解决方法(属于 Web SSO )。

CAS 的单点登录时保障客户端的用户资源的安全 ;OAuth2 则是保障服务端的用户资源的安全 。

CAS 客户端要获取的最终信息是,这个用户到底有没有权限访问我(CAS 客户端)的资源;OAuth2 获取的最终信息是,我(oauth2 服务提供方)的用户的资源到底能不能让你(oauth2 的客户端)访问。

因此,需要统一的账号密码进行身份认证,用 CAS;需要授权第三方服务使用我方资源,使用 OAuth2。

好了,不知道大家对 SSO 是否有了更深刻的理解,欢迎留言。

相关文章
|
算法 Java 应用服务中间件
cas5.3:CAS Server搭建
cas5.3:CAS Server搭建
1610 0
|
4月前
|
弹性计算 人工智能 Linux
OpenClaw是什么?OpenClaw入门详解、阿里云ECS/本地部署及免费大模型配置教程
OpenClaw(社区俗称Clawdbot,龙虾)是一款基于MIT开源协议开发的自托管AI智能体执行网关,核心价值在于打破传统AI“只会说不会做”的局限,连接大语言模型与设备系统、工具应用,将自然语言指令转化为可落地的实操动作,实现从需求下达、任务拆解到执行反馈的全流程闭环。与单纯输出文字的对话式AI不同,OpenClaw可直接操控终端、管理文件、自动化浏览器操作,兼顾数据隐私与执行效率,无需专业技术背景,零基础用户也能快速部署使用。
1788 5
|
4月前
|
存储 人工智能 自然语言处理
一文搞懂!OpenClaw到底是什么?可以做什么?怎样去部署?
2026年初走红的开源AI智能体OpenClaw(昵称“小龙虾”),主打“用自然语言指挥电脑”,支持本地部署、系统级操作与跨应用自动化。具备文件管理、邮件处理、代码辅助、客服响应、风控预警等实用能力,兼顾隐私安全与低门槛部署,是个人与企业专属的私有化“数字员工”。
2436 2
|
4月前
|
人工智能 Linux API
全平台部署OpenClaw指南|阿里云+本地三系统+千问/Coding Plan API配置+避坑全解
2026年,OpenClaw(原Clawdbot)凭借“指令驱动+工具执行”的核心优势,从技术极客的探索工具升级为全球通用的AI基础设施,被国际数据科学媒体KDnuggets评为“2026年最火的免费AI Agent工具”。与传统聊天式AI不同,OpenClaw并非单一模型,而是一个能连接各类大语言模型、实现终端执行、文件读写、跨应用协同的“万能适配器”,通过可插拔的Skill生态,让AI从“提供建议”升级为“落地执行”,真正成为能干活的“数字员工”。
449 0
|
SQL API 容器
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:后端项目结构说明
GoWind Admin|风行是一款企业级前后端一体中后台框架,后端采用Go语言,基于Protobuf定义API,支持Buf代码生成与Docker部署。项目结构清晰,含API定义、服务实现、数据库ORM、配置管理及自动化脚本,开箱即用,助力高效开发。
587 0
|
前端开发 容器
css的布局方式有哪些,有什么优缺点
css的布局方式有哪些,有什么优缺点
494 0
|
存储 安全 Java
【SSO-CAS部署】安装部署单点登录组件CAS(5.3.x版本)
【SSO-CAS部署】安装部署单点登录组件CAS(5.3.x版本)
1637 0
【SSO-CAS部署】安装部署单点登录组件CAS(5.3.x版本)
|
XML 存储 缓存
SpringBoot 使用cas5.3 sso概念
最近一直在集成cas单点登录,这套文章记录一步一步讲解如何详细将cas集成单点登录,所以刚好把这些知识总结一下。如果文章中存在错误,期望大家指出。
1093 0
|
JavaScript 关系型数据库 MySQL
若依框架数据源切换为pg库
若依管理系统切换数据源为pg数据库
3101 1
若依框架数据源切换为pg库