java实现完全跨域SSO单点登录

java实现完全跨域SSO单点登录java 实现 SSO 什么是 SSOSSO SingleSignOn 单点登录是实现多个系统之间统一登录的验证系统 简单来说就是 有 A B C 三个系统 在 A 处登录过后 再访问 B 系统 B 系统就已经处于了登录状态 C 系统也是一样 举个生活中栗子 你同时打开天猫和淘宝 都进入 login 界面 都要求你登录的 现在你在淘宝处登录后 直接在天猫处刷新 你会发现 你已经登录了 而且就是你在淘宝上登录的用户

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangjingao/article/details/

java实现SSO

什么是SSO

SSO(Single Sign On)单点登录是实现多个系统之间统一登录的验证系统,简单来说就是:有A,B,C三个系统,在A处登录过后,再访问B系统,B系统就已经处于了登录状态,C系统也是一样。举个生活中栗子:你同时打开天猫和淘宝,都进入login界面,都要求你登录的,现在你在淘宝处登录后,直接在天猫处刷新,你会发现,你已经登录了,而且就是你在淘宝上登录的用户。说明他们实现了SSO,并且持有相同的信息。

当然这个特性意味着它的使用场景是:同一公司下的不同子系统,因为对于SSO来说,每一个子系统拥有的信息都一样,是用户的全部信息,如果是不同公司,那这肯定不合适。现在的天猫和淘宝就是这样的一套SSO。

实现思想

SSO简单来说就是一句话:一处登录,全部访问。
现在有两个系统分别是:a.comb.com,我们要实现他们的SSO,那么我们就需要一个统一验证中心sso.com,我们所有的登录和身份验证都在sso.com中操作。看图看传统登录方式和SSO方式的差别如下:
传统与SSO方式对比




我们需要将统一信息存在cookie中。

登录部分:

退出部分:

用户点击a.com的退出按钮,访问sso.com/loginout,然后获得所有子系统信息,请求所有子系统clearcookie方法,并重定向到login界面。

跨域部分:我使用的是ajax的jsonp方式。

代码实现

 / * * @return 响应界面:login/index */ @GetMapping("/ssocheck") public ModelAndView checkCookies (HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies != null && cookies.length > 0) { for (Cookie cookie : cookies) { if ("jian".equals(cookie.getName())) { //统一登录cookie为jian,如果存在就认证 log.info("cookie 存在,开始验证"); HttpUtil httpUtil = new HttpUtil("http://sso.com/sso/authcookies", Method.GET); String result = httpUtil.send(cookie.getName(), cookie.getValue()); boolean authBoo = Boolean.valueOf(result); if (authBoo) { log.info("验证通过"); return new ModelAndView("public/index"); } break; } } } return new ModelAndView("index"); } 

在判断中,如果a.com中有名为jian的cookie,那么就去sso.com/sso/authcookies去认证cookievalue,那么在sso.com中方法是这样的:

 / * 验证cookie是否通过 * @param cookieName cookie名称 * @param cookieValue cookie内容 * @return 是否认证成功 */ @GetMapping("/authcookies") public boolean checkAuthCookies (String cookieName, String cookieValue) { boolean isUpdate = new JwtUtil(null,cookieValue).freeJwt(); if ("jian".equals(cookieName) && "ok".equals(cookieValue)) { log.info("cookie验证通过"); return true; } return false; } 

这里用到了HttpUtil类,这是我自己封装的,先说正事,这个工具类下面再放它,不要打扰了主线。如果认证cookie通过,那么说明已经在别的系统处登录过了,然后a.com就返回到首页,如果认证失败,a.com就到达登录页面,在我例子这是分别是public/index和index界面。
登录界面就不亮了,很简单,就两个输入框,输入用户名和密码,然后提交到a.com/login,然后看下这个方法

 / * 登录 * @param username 用户名 * @param password 密码 * @return index/login */ @PostMapping("/login") public ModelAndView doLogin (String username, String password) { if (username != null && !"".equals(username) && password != null && !"".equals(password) ) { HttpUtil httpUtil = new HttpUtil("http://sso.com/sso/", "POST"); Result result = httpUtil.sendLogin(username,password); //如果验证通过,就携带所有子系统域名返回首页 int isLogin = result.getResultCode().getCode(); if (isLogin == 1) { @SuppressWarnings("all") Map 
  
    param = (Map 
   
     ) result.getData(); return new ModelAndView("public/index","sendparam",param); } } return new ModelAndView("index"); } 
    
  

a.com处请求sso.com/sso验证

 / * 统一处理login请求 * @param username 用户名 * @param password 密码 */ @PostMapping public Result 
  
    > checkLogin (String username, String password) { log.info("统一登录校验"); TbUser user = userService.login(username, password); if (user != null) { //封装参数 Map 
   
     param = new HashMap<>(); //获得所有子系统域名信息 List 
    
      domains = domainService.selectAll(); List 
     
       domainUrl = new ArrayList<>(domains.size()); domains.forEach(domain->{ domainUrl.add(domain.getDomain()+"/addcookie"); }); //生成jwt,加密用户信息 String cookieName = "jian"; String cookieValue = new JwtUtil(user.toString(),null).creatJwt(); param.put("cookieurl",domainUrl); param.put("cookieName", cookieName); param.put("cookieValue",cookieValue); Result 
      
        > result = new Result<>(ResultCodeEnum.AUTHSUCCESS); result.setData(param); return result; } return new Result<>(ResultCodeEnum.UNAUTHORIZEd,"账号或密码错误"); } 
       
      
     
    
  

在这里如果验证失败就返回账号或密码错误,如果验证通过,就得到所有域名,然后加密当前用户信息,用到了jwt(json web token,不做过多讲解),然后返回a.com。在a.com发现验证通过就到达首页,验证失败继续到达登录,在首页要使用ajax循环访问所有子系统,将cookie信息添加到所有子系统下。
我的模板引擎使用的是thymeleaf,首页js如下:

  

然后在此访问所有子系统的/addcookie方法,这里涉及到跨域,可以看到,我跨域使用的是ajax的json方式,中间还遇到了一些异常,总之解决掉了。

看下a.com/addcookie方法

 / * * @param cookieName cookie名称 * @param cookieValue cookie值 * @param response 响应 */ @GetMapping("/addcookie") public void addCookies (String cookieName, String cookieValue, HttpServletResponse response) { log.info("添加cookie"); Cookie cookie = new Cookie(cookieName,cookieValue); cookie.setPath("/"); cookie.setMaxAge(3600); cookie.setHttpOnly(true); response.addCookie(cookie); } 

然后这时候查看下浏览器的cookie就会发现已经为它写上了,访问b.com/ssocheck就会直接通过,b.com下也被写了cookie。

 //退出登录,清空所有子系统的cookie $("#loginout").click (function (event) { $.ajax({ url: "http://sso.com/sso/loginout", type: "get", jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback) jsonpCallback:"success_jsonpCallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名 dataType: "jsonp", //指定服务器返回的数据类型 success: function (data) { window.location.href="/loginout"; eachUrl(data);//循环清理掉所有子系统cookie },error:function (data) { console.log(data.jqXHR+" "+data.status+" "+data.error); } }); }); function eachUrl(arrDomain) { jQuery.each(arrDomain, function () { // this 指定值 //循环访问 $.ajax({ url: this, type: "get", dataType: "jsonp" //指定服务器返回的数据类型 }); }); } 

会去访问sso.com/loginout方法,拿到所有域名清除cookie方法。

 / * 添加需要清除的cookie */ @GetMapping("/loginout") public String loginOut (HttpServletRequest request) { String callbackFuncation = request.getParameter("callback"); log.info("start clear"); List 
  
    domains = domainService.selectAll(); List 
   
     domainUrl = new ArrayList<>(domains.size()); domains.forEach(domain->{ domainUrl.add(domain.getDomain()+"/clear"); }); String resultMsg = JSON.toJSONString(domainUrl); return callbackFuncation+"("+resultMsg+")"; } 
    
  

然后拿到后,首先会自己跳回到登录界面,然后再去请求其他子系统的清除cookie方法,防止请求时间过长无法给用户响应。看下清除cookie方法。

 / * 清除掉cookie * @param request 请求 * @param response 响应 */ @GetMapping("/clear") public void clear (HttpServletRequest request,HttpServletResponse response) { //获得域名 log.info("clear掉ip为:"+request.getRemoteHost()+"的cookie"); Cookie [] cookies = request.getCookies(); for (Cookie cookie: cookies) { if ("wlgzs".equals(cookie.getName())) { cookie.setValue(null); cookie.setMaxAge(0); response.addCookie(cookie); } } } 

然后再去访问刚才还能访问的b.com就会发现验证失败返回到登录界面,它的cookie也被清除了。

我更新了另一篇关于SSO的文章,想做单点登录的我建议看看,另一个版本是我目前在使用的版本。
链接

:java实现 SSO 单点登录(最终版)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203000.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午10:49
下一篇 2026年3月19日 下午10:49


相关推荐

  • L3-002 特殊堆栈(树状数组+二分)「建议收藏」

    L3-002 特殊堆栈(树状数组+二分)「建议收藏」原题链接堆栈是一种经典的后进先出的线性结构,相关的操作主要有“入栈”(在堆栈顶插入一个元素)和“出栈”(将栈顶元素返回并从堆栈中删除)。本题要求你实现另一个附加的操作:“取中值”——即返回所有堆栈中元素键值的中值。给定 N 个元素,如果 N 是偶数,则中值定义为第 N/2 小元;若是奇数,则为第 (N+1)/2 小元。输入格式:输入的第一行是正整数 N(≤10​5​​ )。随后 N 行,每行给出一句指令,为以下 3 种之一:Push keyPopPeekMedian其中 key 是不超过

    2022年8月9日
    7
  • BeanUtils.populate()的作用

    BeanUtils.populate()的作用//1.获得请求参数Map<String,String[]>map=request.getParameterMap();Useruser=newUser();//

    2022年7月4日
    28
  • Hbase面试题(面经)整理

    Hbase面试题(面经)整理1.Hbase是什么?hbase的特点是什么?Hbase一个分布式的基于列式存储的数据库,基于Hadoop的hdfs存储,zookeeper进行管理。 Hbase适合存储半结构化或非结构化数据,对于数据结构字段不够确定或者杂乱无章很难按一个概念去抽取的数据。 Hbase为null的记录不会被存储。 基于的表包含rowkey,时间戳,和列族。新写入数据时,时间戳更新,同…

    2022年5月31日
    39
  • 3D打印S3d参数设置

    3D打印S3d参数设置挤出机层附加填充温度冷却脚本开始脚本 G28 homeallaxes 结束脚本 M104S0 extruderheat relativeposi 1F300 retractthefi 0 5E 5X 20Y 20F8700 moveZupa

    2026年3月26日
    2
  • 搭建网络SDN(企业网络环境搭建)

    1.     搭建环境要求:图1中控制器可以自主选择,既可选择各种开源的控制器(例如:Floodlight、Ryu、Nox、Beacon、Trema、OpenDaylight等),也可选择由本次大赛设备提供商所提供的闭源控制器。拓扑中各网络部件既可以是仿真环境实现(例如mininet,OpenvSwtich),有条件的队伍也可以通过物理设备实现,两种方案不影响必答题的评分。2.     操作

    2022年4月11日
    236
  • AI编程⑬:Cursor如何开发前后端分离项目

    AI编程⑬:Cursor如何开发前后端分离项目

    2026年3月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号