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

本文涉及的产品
云数据库 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 是否有了更深刻的理解,欢迎留言。

相关文章
|
安全 关系型数据库 MySQL
公司内部搭建企业论坛,并实现在外也可以访问
公司内部搭建企业论坛,并实现在外也可以访问
207 0
|
网络协议 数据安全/隐私保护 Windows
当不在公司时,如何在外远程登录公司内网OA系统?
当不在公司时,如何在外远程登录公司内网OA系统?
359 0
|
存储 消息中间件 安全
老板:公司系统太多,能不能实现账号互通?(一)
最近开发新产品,然后老板说我们现在系统太多了,每次切换系统登录太麻烦了,能不能做个优化,同一账号互通掉。作为一个资深架构狮,老板的要求肯定要满足,安排!
老板:公司系统太多,能不能实现账号互通?(一)
|
Rust 数据可视化 安全
95后百度员工对领导不满,删改公司数据库被判刑;微软在美取消竞业协议;TikTok中国管理团队与海外员工冲突引发离职潮 |Q资讯
95 后百度员工因项目被接手对领导不满,刻意删改公司数据库被判刑;微软宣布在美国停止执行员工竞业协议;字节跳动员工“秘密”入职快手,被判返赔近 38 万;特斯拉将裁员 1 万人、暂停全球招聘,马斯克回应:裁员不涉及实际生产人员,还将增加小时工;TikTok 中国管理团队与英国员工发生矛盾冲突,众多员工离职,涉事高管被停职调查;蚂蚁集团:目前没有启动 IPO 的计划;无视微软反对,美商务部限制中美网络安全合作;GitHub Atom 将停用,6 个月后完成归档......
283 0
|
Web App开发 自然语言处理 安全
TikTok 美国员工抱怨工作压力大;多省集中排查整治超时加班;黑客向上海市民卖抢菜软件被抓;苹果员工对回办公室工作感到不满
本周架构视点:TikTok 美国员工抱怨工作压力大:需周末加班,跨时区工作;谷歌调整绩效评价体系:降低评估频率,属于变相加薪;多个省份集中排查整治超时加班问题,互联网企业等被重点检查;向日葵远程控制回应服务器崩溃;GitHub 要求所有贡献代码的用户在 2023 年底前启用双因素认证;WhatsApp 前高管:后悔当年把公司卖给 Facebook。
262 0
|
存储 运维 大数据
Facebook运维内幕曝光:一人管理2万台服务器
Facebook 数据中心运维主管 Delfina Eberly 目前,Facebook 已经凭借它在网络基础建设上的可扩展能力成为了行业的领军者。Facebook 数据中心运维主管 Delfina Eberly(上图人物) 在“7x24 Exchange 2013 秋季会议”上的演讲中为我们透露了 Facebook 部分内部运维数据,下面我们来具体了解下。
342 0
Facebook运维内幕曝光:一人管理2万台服务器
|
运维 安全 数据库
微盟突遭员工“删库跑路”!疫情之下远程运维首重安全
安全运维凭自觉?NO! 制度和技术才是保障
微盟突遭员工“删库跑路”!疫情之下远程运维首重安全
|
安全 数据安全/隐私保护 网络协议

热门文章

最新文章