前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SSO 单点登录_sso登陆

SSO 单点登录_sso登陆

作者头像
全栈程序员站长
发布2022-11-01 16:59:43
1.1K0
发布2022-11-01 16:59:43
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。实现 SSO 的主要工具是 Cookie

实现步骤

简的来说:申请票据、存储票据、查验票据。

同域 SSO 登录

此处使用 struts2 作为 MVC 框架,可根据实际需要替换

登录界面

接收用户登录信息

代码语言:javascript
复制
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, inital-scale=1">
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<center>
<h1>请登录</h1>
<form action="/sso/doLogin.action" method="post">
<span>用户名:</span><input type="text" name="username">
<span>密码&nbsp;&nbsp;:</span><input type="password" name="password"><br>
<%-- gotoUrl 包含从其它页面过来的地址,登录完毕后跳转回 gotoUrl --%>
<input type="hidden" name="gotoUrl" value="${gotoUrl}">
<input type="submit">
</form>
</center>
</body>
</html>

处理用户登录信息

代码语言:javascript
复制
import com.opensymphony.xwork2.ActionSupport;
import lee.sso.util.SSOCheck;
import org.apache.struts2.ServletActionContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
public class SSOAction extends ActionSupport { 

private String username;
private String password;
private String gotoUrl;
public String doLogin() {
/* 此处不进行数据用户数据验证, 只关注 sso */
boolean ok = SSOCheck.checkLogin(username, password);
if (ok) {
/* 此处 cookie 值应为加密后的值,此处亦省去 */
Cookie cookie = new Cookie("ssocookie", "sso");
cookie.setPath("/");
HttpServletResponse response = ServletActionContext.getResponse();
response.addCookie(cookie);
return SUCCESS;
}
// 失败此处不做处理,或者返回登录页面
return null;
}
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 getGotoUrl() {
return gotoUrl;
}
public void setGotoUrl(String gotoUrl) {
this.gotoUrl = gotoUrl;
}
}

处理登录信息工具类

代码语言:javascript
复制
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
public class SSOCheck { 

public static final String USERNAME = "user";
public static final String PASSWORD = "123";
public static boolean checkLogin(String username, String password) {
return username.equals(USERNAME) && password.equals(PASSWORD) ? true
: false;
}
/** * 实际开发时应放在拦截器,此处为了方便 * * @param request * @return */
public static boolean checkCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("ssocookie") && cookie.getValue()
.equals("sso"))
return true;
}
}
return false;
}
}

登录页面的 struts2 配置

代码语言:javascript
复制
<package name="sso" namespace="/sso" extends="struts-default">
<action name="doLogin" class="lee.sso.action.SSOAction" method="doLogin">
<result name="success" type="redirect">${gotoUrl}</result>
</action>
</package>

编写 domain1

主页,当为用户访问时未登录将先跳转登录页面

代码语言:javascript
复制
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, inital-scale=1">
<meta charset="UTF-8">
<title>domain1 的标题</title>
</head>
<body>
<h1>欢迎访问 domain1,这是 domain1 的主页</h1>
</body>
</html>

domain1 Action 类

代码语言:javascript
复制
import com.opensymphony.xwork2.ActionSupport;
import lee.sso.util.SSOCheck;
import org.apache.struts2.ServletActionContext;
import javax.servlet.http.HttpServletRequest;
public class Domain1Action extends ActionSupport { 

private String gotoUrl;
public String main() {
HttpServletRequest request = ServletActionContext.getRequest();
if (SSOCheck.checkCookie(request)) {
return SUCCESS;
}
// 此处声明登录完成后跳转的页面
gotoUrl = "/domain1/main.action";
return LOGIN;
}
public String getGotoUrl() {
return gotoUrl;
}
public void setGotoUrl(String gotoUrl) {
this.gotoUrl = gotoUrl;
}
}

domain1 的 struts2 配置

代码语言:javascript
复制
<package name="domain1" namespace="/domain1" 
extends="struts-default">
<action name="main" class="domain1.Domain1Action"
method="main">
<result name="success">/domain1.jsp</result>
<result name="login">/login.jsp</result>
</action>
</package>

domain2 与 domain1 区别不大,只需将 1 复制一份,将 1 改为 2 即可。

效果

填写 domain1 (别点提交)

进入 domain2 看看

domain1 点解提交

此时刷新 domain2 登录界面

神奇的一幕发生了,不需要登录信息直接进入 domain2 的主页

这是因为在登录 domain1 后服务器向我们发送了一个 cookie,浏览器保存了。当刷新 domain2 时,浏览器带着未过期的 cookie 给服务器发请求,服务器判断此 cookie 的会话是否还有效,若有,则直接跳过登录页面进入 domain2 主页。


同父域 SSO 登录

若在同一机器部署测试,可更改文件C:\Windows\System32\drivers\etc\hosts文件

由图可看出,同父域的 SSO 登录与同域 SSO 登录并无多大区别,需要解决的问题有两个,一个是 cookie 的跨域问题以及服务器之间的通信。可根据同域 SSO 登录修改代码。

cookie 跨域

此处主要使用了子域可访问父域cookie的机制。设置 cookie 时,需要我们统一一个 token ,即 cookie 的变量名。

我们访问http://demo1.x.com/demo1/main.action时,查看 request.getCookies()是否包含我们所需的信息,若无,则带着验证后跳转地址<input type="hidden" name="gotoUrl">跳转到统一验证接口http://check.x.com/sso/doLogin.action验证用户名和密码。

统一验证接口返回 cookie 之前,必须设置

代码语言:javascript
复制
// 由 . 开头,则只有子域名可用。若无,则主域名和子域名可用
cookie.setDomain(".x.com");  // 同一父域名
cookie.setPath("/");

浏览器保存cookie后,带着cookie跳转回 http://demo1.x.com/demo1/main.action 要设置setDomain(),否则请求无法携带cookie

服务器之间的通信

http://demo1.x.com/demo1/main.action的服务器接收到cookie时,将cookie名cookie值发给统一验证接口http://check.x.com/sso/checkCookie.action处理,约定好返回值是什么,成功返回后判断是否让其访问资源。

可通过 HttpURLConnection 类或者使用 HttpClient 客户端进行通信。


完全跨域 SSO 登录

即域名完全不同,如www.a.comwww.b.com

登录验证

进入www.a.com资源页面,查看cookie,若无,则跳转登录页面。

www.a.com服务器接收用户信息数据,但不验证,将用户信息数据传输http://www.x.com/sso/doLogin.action验证。

代码语言:javascript
复制
<form action="/doLogin.action"></form>

进入 action 类,根据代码逻辑与验证服务器通信

代码语言:javascript
复制
result = SSOUtil.post(checkSSOUrl, username, password);

验证成功后返回信息给www.a.com,判断可访问资源后并向浏览器发送 cookie 存放确认用户身份的 token(即 cookieValue)

代码语言:javascript
复制
Cookie cookie = new Cookie(cookieName, cookieValue);
HttpServletResponse response = ServletActionContext.getResponse();
response.addCookie(cookie);

分发 cookie

问题此cookie只有www.a.com才能访问,所以,需要在确定用户身份后,访问资源页面时向www.b.com发送请求,并让www.b.com向浏览器发送cookie的值

www.a.comwww.b.comaction类里添加方法,传送兄弟 URL 和传参,则无论是访问 a 或 b ,登录成功后在cookie有效期内都能访问 b 或 a 的资源。

代码语言:javascript
复制
List<String> urlList; // 存放兄弟 URL
String cookieName;
String cookieValue;
...
// Struts2 的 OGNL
setter/getter
...
public String main() {
...
urlList.add("www.b.com/demo2/pushCookieToBrowser.action"
+ "?cookieName=" + cookieName
+ "&cookieValue=" + cookieValue);
...
}
public void pushCookieToBrowser() {
HttpServletRequest request = ServletActionContext.getRequest();
this.cookieName = request.getParameter("cookieName");
this.cookieValue = request.getParameter("cookieValue");
Cookie cookie = new Cookie(cookieName, cookieValue);
HttpServletResponse response = ServletActionContext.getResponse();
response.addCookie(cookie);
}

在登录成功的资源页面使用 <iframe> 将返回数据信息循环请求发给兄弟服务器域名,让兄弟域名向浏览器发送 cookie,完成跨域 SSO

www.a.com loginSuccess.jsp

代码语言:javascript
复制
<c:forEach var="url" items="${urlList}">
<%-- 隐藏 iframe --%>
<iframe src="${url}" width="0px" hight="0px" style="dispaly: none"></iframe>
</c:forEach>

再访问 b 的资源时将不需要再次输入密码。


注意

  1. 核心是Cookie,需要注意设置的域、位置和安全性。
  2. 应用群的安全问题:木桶效应。

资料

慕课视频

单点登录原理与简单实现

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 同域 SSO 登录
    • 登录界面
      • 编写 domain1
        • 效果
        • 同父域 SSO 登录
          • cookie 跨域
            • 服务器之间的通信
            • 完全跨域 SSO 登录
              • 登录验证
                • 分发 cookie
                • 注意
                相关产品与服务
                访问管理
                访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档