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

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
.cn 域名,1个 12个月
简介: 老板:公司系统太多,能不能实现账号互通?(二)

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 是否有了更深刻的理解,欢迎留言。

相关文章
|
网络协议 数据安全/隐私保护 Windows
当不在公司时,如何在外远程登录公司内网OA系统?
当不在公司时,如何在外远程登录公司内网OA系统?
306 0
|
5月前
希望阿里的小伙伴在控制台的易用性多上点心,每次问客服好像都是外包人员,啥也不会
希望阿里的小伙伴在控制台的易用性多上点心,每次问客服好像都是外包人员,啥也不会
140 2
|
5月前
|
存储 数据采集 运维
云效产品使用报错问题之给公司同事配置了权限,但是看不见项目,如何解决
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。
|
5月前
|
消息中间件 缓存 监控
直呼内行!阿里大佬离职带出内网专属“高并发系统设计”学习笔记
我们知道,高并发代表着大流量,高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案,从而抵抗巨大流量的冲击,带给用户更好的使用体验。这些方案好似能操纵流量,让流量更加平稳得被系统中的服务和组件处理。
|
10月前
|
人工智能 供应链 搜索推荐
代购系统独立站的未来发展前景
随着全球互联网的普及和电子商务的飞速发展,代购行业也呈现出迅猛增长的态势。代购系统独立站作为代购行业的重要组成部分,以其独特的商业模式和运营方式,吸引了越来越多的用户和投资者的关注。本文将从多个角度探讨代购系统独立站的未来发展前景,并分析其面临的挑战和机遇。
|
网络协议 应用服务中间件 数据库
如何在出差期间远程访问企业ERP系统?内网穿透解决您的难题!
如何在出差期间远程访问企业ERP系统?内网穿透解决您的难题!
163 0
|
SQL JSON 前端开发
校园外卖点餐系统——Day02【员工管理业务开发】
校园外卖点餐系统——Day02【员工管理业务开发】
145 0
校园外卖点餐系统——Day02【员工管理业务开发】
|
存储 消息中间件 安全
老板:公司系统太多,能不能实现账号互通?(一)
最近开发新产品,然后老板说我们现在系统太多了,每次切换系统登录太麻烦了,能不能做个优化,同一账号互通掉。作为一个资深架构狮,老板的要求肯定要满足,安排!
老板:公司系统太多,能不能实现账号互通?(一)
|
Rust 数据可视化 安全
95后百度员工对领导不满,删改公司数据库被判刑;微软在美取消竞业协议;TikTok中国管理团队与海外员工冲突引发离职潮 |Q资讯
95 后百度员工因项目被接手对领导不满,刻意删改公司数据库被判刑;微软宣布在美国停止执行员工竞业协议;字节跳动员工“秘密”入职快手,被判返赔近 38 万;特斯拉将裁员 1 万人、暂停全球招聘,马斯克回应:裁员不涉及实际生产人员,还将增加小时工;TikTok 中国管理团队与英国员工发生矛盾冲突,众多员工离职,涉事高管被停职调查;蚂蚁集团:目前没有启动 IPO 的计划;无视微软反对,美商务部限制中美网络安全合作;GitHub Atom 将停用,6 个月后完成归档......
271 0
|
存储 运维 大数据
Facebook运维内幕曝光:一人管理2万台服务器
Facebook 数据中心运维主管 Delfina Eberly 目前,Facebook 已经凭借它在网络基础建设上的可扩展能力成为了行业的领军者。Facebook 数据中心运维主管 Delfina Eberly(上图人物) 在“7x24 Exchange 2013 秋季会议”上的演讲中为我们透露了 Facebook 部分内部运维数据,下面我们来具体了解下。
319 0
Facebook运维内幕曝光:一人管理2万台服务器
下一篇
无影云桌面