大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。
:单点登录(
SSO
)的技术被越来越广泛地运用到各个领域的软件系统其中。本文从业务的角度分析了单点登录的需求和应用领域;从技术本身的角度分析了单点登录技术的内部机制和实现手段,而且给出
Web-SSO
和桌面
SSO
的实现、源码和具体解说;还从安全和性能的角度对现有的实现技术进行进一步分析,指出对应的风险和须要改进的方面。本文除了从多个方面和角度给出了对单点登录(
SSO
)的全面分析,还而且讨论了怎样将现有的应用和
SSO
服务结合起来,可以帮助应用架构师和系统分析人员从本质上认识单点登录,从而更好地设计出符合须要的安全架构。
:
SSO, Java, J2EE, JAAS
什么是单点登陆
Single Sign On
),简称为
SSO
,是眼下比較流行的企业业务整合的解决方式之中的一个。
SSO
的定义是在多个应用系统中,用户仅仅须要登录一次就能够訪问全部相互信任的应用系统。
IT
服务。比如財务系统为財务人员提供財务的管理、计算和报表服务;人事系统为人事部门提供全公司人员的维护服务;各种业务系统为公司内部不同的业务提供不同的服务等等。这些系统的目的都是让计算机来进行复杂繁琐的计算工作,来替代人力的手工劳动,提高工作效率和质量。这些不同的系统往往是在不同的时期建设起来的,执行在不同的平台上;或许是由不同厂商开发,使用了各种不同的技术和标准。假设举例说国内一著名的
IT
公司(名字隐去),内部共同拥有
60
多个业务系统,这些系统包含两个不同版本号的
SAP
的
ERP
系统,
12
个不同类型和版本号的数据库系统,
8
个不同类型和版本号的操作系统,以及使用了
3
种不同的防火墙技术,还有数十种互相不能兼容的协议和标准,你相信吗?不要怀疑,这样的情况事实上非常普遍。每个应用系统在执行了数年以后,都会成为不可替换的企业
IT
架构的一部分,例如以下图所看到的。
EAI
)。企业应用集成能够在不同层面上进行:比如在数据存储层面上的“数据大集中”,在传输层面上的“通用数据交换平台”,在应用层面上的“业务流程整合”,和用户界面上的“通用企业门户”等等。其实,还用一个层面上的集成变得越来越重要,那就是“身份认证”的整合,也就是“单点登录”。
- 用户每天平均 16 分钟花在身份验证任务上 – 资料来源: IDS
- 频繁的 IT 用户平均有 21 个password – 资料来源: NTA Monitor Password Survey
- 49% 的人写下了其password,而 67% 的人非常少改变它们
- 每 79 秒出现一起身份被窃事件 – 资料来源:National Small Business Travel Assoc
- 全球欺骗损失每年约 12B – 资料来源:Comm Fraud Control Assoc
- 到 2007 年,身份管理市场将成倍增长至 $4.5B – 资料来源:IDS
- 提高 IT 效率:对于每 1000 个受管用户,每用户可节省$70K
- 帮助台呼叫降低至少1/3,对于 10K 员工的公司,每年能够节省每用户 $75,或者合计 $648K
- 生产力提高:每一个新员工可节省 $1K,每一个老员工可节省 $350 �资料来源:Giga
- ROI 回报:7.5 到 13 个月 �资料来源:Gartner
SOA
时代的需求之中的一个。在面向服务的架构中,服务和服务之间,程序和程序之间的通讯大量存在,服务之间的安全认证是
SOA
应用的难点之中的一个,应此建立“单点登录”的系统体系可以大大简化
SOA
的安全问题,提高服务之间的合作效率。
单点登陆的技术实现机制
SSO
技术的流行,
SSO
的产品也是满天飞扬。全部著名的软件厂商都提供了对应的解决方式。在这里我并不想介绍自己公司(
Sun Microsystems
)的产品,而是对
SSO
技术本身进行解析,而且提供自己开发这一类产品的方法和简单演示。有关我写这篇文章的目的,请參考我的博客(
http://yuwang881.blog.sohu.com/3184816.html
)。
1
的时候,由于还没有登录,会被引导到认证系统中进行登录(
1
);依据用户提供的登录信息,认证系统进行身份效验,假设通过效验,应该返回给用户一个认证的凭据--
ticket
(
2
);用户再訪问别的应用的时候(
3
,
5
)就会将这个
ticket
带上,作为自己认证的凭据,应用系统接受到请求之后会把
ticket
送到认证系统进行效验,检查
ticket
的合法性(
4
,
6
)。假设通过效验,用户就能够在不用再次登录的情况下訪问应用系统
2
和应用系统
3
了。
SSO
,须要下面基本的功能:
- 全部应用系统共享一个身份认证系统。
统一的认证系统是SSO的前提之中的一个。认证系统的主要功能是将用户的登录信息和用户信息库相比較,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行效验,推断其有效性。 - 全部应用系统可以识别和提取ticket信息
要实现SSO的功能,让用户仅仅登录一次,就必须让应用系统可以识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自己主动推断当前用户是否登录过,从而完毕单点登录的功能。
SSO
架构,在现实情况下的
SSO
有着更加复杂的结构。有两点须要指出的是:
- 单一的用户信息数据库并非必须的,有很多系统不能将全部的用户信息都集中存储,应该同意用户信息放置在不同的存储中,例如以下图所看到的。其实,仅仅要统一认证系统,统一ticket的产生和效验,不管用户信息存储在什么地方,都能实现单点登录。
- 统一的认证系统并非说仅仅有单个的认证server,例如以下图所看到的,整个系统可以存在两个以上的认证server,这些server甚至可以是不同的产品。认证server之间要通过标准的通讯协议,互相交换认证信息,就能完毕更高级别的单点登录。例如以下图,当用户在訪问应用系统1时,由第一个认证server进行认证后,得到由此server产生的ticket。当他訪问应用系统4的时候,认证server2可以识别此ticket是由第一个server产生的,通过认证server之间标准的通讯协议(比如SAML)来交换认证信息,仍然可以完毕SSO的功能。
的实现
WEB
应用差点儿统治了绝大部分的软件应用系统,因此
WEB-SSO
是
SSO
应用其中最为流行。
WEB-SSO
有其自身的特点和优势,实现起来比較简单易用。非常多商业软件和开源软件都有对
WEB-SSO
的实现。其中值得一提的是
OpenSSO
(
https://opensso.dev.java.net
),为用
Java
实现
WEB-SSO
提供架构指南和服务指南,为用户自己来实现
WEB-SSO
提供了理论的根据和实现的方法。
WEB-SSO
比較easy实现呢?这是有
WEB
应用自身的特点决定的。
Web
协议(也就是
HTTP
)是一个无状态的协议。一个
Web
应用由非常多个
Web
页面组成,每一个页面都有唯一的
URL
来定义。用户在浏览器的地址栏输入页面的
URL
,浏览器就会向
Web Server
去发送请求。例如以下图,浏览器向
Web
server发送了两个请求,申请了两个页面。这两个页面的请求是分别使用了两个单独的
HTTP
连接。所谓无状态的协议也就是表如今这里,浏览器和
Web
server会在第一个请求完毕以后关闭连接通道,在第二个请求的时候又一次建立连接。
Web
server并不区分哪个请求来自哪个client,对全部的请求都一视同仁,都是单独的连接。这种方式大大差别于传统的(
Client/Server
)
C/S
结构
,
在那样的应用中,client和server端会建立一个长时间的专用的连接通道。正是由于有了无状态的特性,每一个连接资源可以非常快被其它client所重用,一台
Web
server才可以同一时候服务于成千上万的client。
SSO
,在同一个应用中也须要保存用户的登录身份信息。比如用户在訪问页面
1
的时候进行了登录,可是刚才也提到,client的每一个请求都是单独的连接,当客户再次訪问页面
2
的时候,怎样才干告诉
Web
server,客户刚才已经登录过了呢?浏览器和server之间有约定:通过使用
cookie
技术来维护应用的状态。
Cookie
是能够被
Web
server设置的字符串,而且能够保存在浏览器中。例如以下图所看到的,当浏览器訪问了页面
1
时,
web
server设置了一个
cookie
,并将这个
cookie
和页面
1
一起返回给浏览器,浏览器接到
cookie
之后,就会保存起来,在它訪问页面
2
的时候会把这个
cookie
也带上,
Web
server接到请求时也能读出
cookie
的值,依据
cookie
值的内容就能够推断和恢复一些用户的信息状态。
全然能够利用
Cookie
结束来完毕用户登录信息的保存,将浏览器中的
Cookie
和上文中的
Ticket
结合起来,完毕
SSO
的功能。
SSO
的功能,须要两个部分的合作:
- 统一的身份认证服务。
- 改动Web应用,使得每一个应用都通过这个统一的认证服务来进行身份效验。
的例子
J2EE
的技术(
JSP
和
Servlet
)完毕了一个具有
Web-SSO
的简单例子。例子包括一个身份认证的server和两个简单的
Web
应用,使得这两个
Web
应用通过统一的身份认证服务来完毕
Web-SSO
的功能。此例子全部的源码和二进制代码都能够从站点地址
http://gceclub.sun.com.cn/wangyu/
下载。
- Web-SSO的例子是由三个标准Web应用组成,压缩成三个zip文件,从http://gceclub.sun.com.cn/wangyu/web-sso/中下载。当中SSOAuth(http://gceclub.sun.com.cn/wangyu/web-sso/SSOAuth.zip)是身份认证服务;SSOWebDemo1(http://gceclub.sun.com.cn/wangyu/web-sso/SSOWebDemo1.zip)和SSOWebDemo2(http://gceclub.sun.com.cn/wangyu/web-sso/SSOWebDemo2.zip)是两个用来演示单点登录的Web应用。这三个Web应用之所以没有打成war包,是由于它们不能直接部署,依据读者的部署环境须要作出小小的改动。例子部署和执行的环境有一定的要求,须要符合Servlet2.3以上标准的J2EE容器才干执行(比如Tomcat5,Sun Application Server 8, Jboss 4等)。另外,身份认证服务须要JDK1.5的执行环境。之所以要用JDK1.5是由于笔者使用了一个线程安全的高性能的Java集合类“ConcurrentMap”,仅仅有在JDK1.5中才有。
- 这三个Web应用全然能够单独部署,它们能够分别部署在不同的机器,不同的操作系统和不同的J2EE的产品上,它们全然是标准的和平台无关的应用。可是有一个限制,那两台部署应用(demo1、demo2)的机器的域名须要同样,这在后面的章节中会解释到cookie和domain的关系以及怎样制作跨域的WEB-SSO
- 解压缩SSOAuth.zip文件,在/WEB-INF/下的web.xml中请改动“domainname”的属性以反映实际的应用部署情况,domainname须要设置为两个单点登录的应用(demo1和demo2)所属的域名。这个domainname和当前SSOAuth服务部署的机器的域名没有关系。我缺省设置的是“.sun.com”。假设你部署demo1和demo2的机器没有域名,请输入IP地址或主机名(如localhost),可是假设使用IP地址或主机名也就意味着demo1和demo2须要部署到一台机器上了。设置完后,依据你所选择的J2EE容器,可能须要将SSOAuth这个文件夹压缩打包成war文件。用“jar -cvf SSOAuth.war SSOAuth/”就能够完毕这个功能。
- 解压缩SSOWebDemo1和SSOWebDemo2文件,分别在它们/WEB-INF/下找到web.xml文件,请改动当中的几个初始化參数
<init-param>
<param-name>SSOServiceURL</param-name>
<param-value>http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth</param-value>
</init-param>
<init-param>
<param-name>SSOLoginPage</param-name>
<param-value>http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp</param-value>
</init-param>
将当中的SSOServiceURL和SSOLoginPage改动成部署SSOAuth应用的机器名、port号以及根路径(缺省是SSOAuth)以反映实际的部署情况。设置完后,依据你所选择的J2EE容器,可能须要将SSOWebDemo1和SSOWebDemo2这两个文件夹压缩打包成两个war文件。用“jar -cvf SSOWebDemo1.war SSOWebDemo1/”就能够完毕这个功能。 - 请输入第一个web应用的測试URL(test.jsp),比如http://wangyu.prc.sun.com:8080/ SSOWebDemo1/test.jsp,假设是第一次訪问,便会自己主动跳转到登录界面,例如以下图
- 使用系统自带的三个帐号之中的一个登录(比如,username:wangyu,password:wangyu),便能成功的看到test.jsp的内容:显示当前username和欢迎信息。
- 请接着在同一个浏览器中输入第二个web应用的測试URL(test.jsp),比如http://wangyu.prc.sun.com:8080/ SSOWebDemo2/test.jsp。你会发现,不须要再次登录就能看到test.jsp的内容,相同是显示当前username和欢迎信息,并且欢迎信息中明白的显示当前的应用名称(demo2)。
代码解说
身份认证服务代码解析
的源码能够从站点地址
http://gceclub.sun.com.cn/wangyu/web-sso/websso_src.zip
下载。身份认证服务是一个标准的
web
应用,包含一个名为
SSOAuth
的
Servlet
,一个
login.jsp
文件和一个
failed.html
。身份认证的全部服务差点儿都由
SSOAuth
的
Servlet
来实现了;
login.jsp
用来显示登录的页面(假设发现用户还没有登录过);
failed.html
是用来显示登录失败的信息(假设用户的username和password与信息数据库中的不一样)。
的代码如以下的列表显示,结构很easy,先看看这个
Servlet
的主体部分:
package DesktopSSO; import java.io.*; import java.net.*; import java.text.*; import java.util.*; import java.util.concurrent.*; import javax.servlet.*; import javax.servlet.http.*; public class SSOAuth extends HttpServlet { static private ConcurrentMap accounts; static private ConcurrentMap SSOIDs; String cookiename=”WangYuDesktopSSOID”; String domainname; public void init(ServletConfig config) throws ServletException { super.init(config); domainname= config.getInitParameter(“domainname”); cookiename = config.getInitParameter(“cookiename”); SSOIDs = new ConcurrentHashMap(); accounts=new ConcurrentHashMap(); accounts.put(“wangyu”, “wangyu”); accounts.put(“paul”, “paul”); accounts.put(“carol”, “carol”); } protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); String action = request.getParameter(“action”); String result=”failed”; if (action==null) { handlerFromLogin(request,response); } else if (action.equals(“authcookie”)){ String myCookie = request.getParameter(“cookiename”); if (myCookie != null) result = authCookie(myCookie); out.print(result); out.close(); } else if (action.equals(“authuser”)) { result=authNameAndPasswd(request,response); out.print(result); out.close(); } else if (action.equals(“logout”)) { String myCookie = request.getParameter(“cookiename”); logout(myCookie); out.close(); } } ….. } |
SSOAuth
就是一个简单的
Servlet
。当中有两个静态成员变量:
accounts
和
SSOIDs
,这两个成员变量都使用了
JDK1.5
中线程安全的
MAP
类: ConcurrentMap
,所以这个例子一定要
JDK1.5
才干执行。
Accounts
用来存放用户的username和password,在
init()
的方法中能够看到我给系统加入�了三个合法的用户。在实际应用中,
accounts
应该是去数据库中或
LDAP
中获得,为了简单起见,在本例子中我使用了
ConcurrentMap
在内存中用程序创建了三个用户。而
SSOIDs
保存了在用户成功的登录后所产生的
cookie
和username的相应关系。它的功能显而易见:当用户成功登录以后,再次訪问别的系统,为了鉴别这个用户请求所带的
cookie
的有效性,须要到
SSOIDs
中检查这种映射关系是否存在。
processRequest()
中,能够非常清楚的看到
SSOAuth
的全部功能
- 假设用户还没有登录过,是第一次登录本系统,会被跳转到login.jsp页面(在后面会解释怎样跳转)。用户在提供了username和password以后,就会用handlerFromLogin()这种方法来验证。
- 假设用户已经登录过本系统,再訪问别的应用的时候,是不须要再次登录的。由于浏览器会将第一次登录时产生的cookie和请求一起发送。效验cookie的有效性是SSOAuth的主要功能之中的一个。
- SSOAuth还能直接效验非login.jsp页面过来的username和password的效验请求。这个功能是用于非web应用的SSO,这在后面的桌面SSO中会用到。
- SSOAuth还提供logout服务。
private void handlerFromLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter(“username”); String password = request.getParameter(“password”); String pass = (String)accounts.get(username); if ((pass==null)||(!pass.equals(password))) getServletContext().getRequestDispatcher(“/failed.html”).forward(request, response); else { String gotoURL = request.getParameter(“goto”); String newID = createUID(); SSOIDs.put(newID, username); Cookie wangyu = new Cookie(cookiename, newID); wangyu.setDomain(domainname); wangyu.setMaxAge(60000); wangyu.setValue(newID); wangyu.setPath(“/”); response.addCookie(wangyu); System.out.println(“login success, goto back url:” + gotoURL); if (gotoURL != null) { PrintWriter out = response.getWriter(); response.sendRedirect(gotoURL); out.close(); } } } |
这种方法是用来处理来自
login.jsp
的登录请求。它的逻辑非常easy:将用户输入的username和password与预先设定好的用户集合(存放在
accounts
中)相比較,假设username或password不匹配的话,则返回登录失败的页面(
failed.html
),假设登录成功的话,须要为用户当前的
session
创建一个新的
ID
,并将这个
ID
和username的映射关系存放到
SSOIDs
中,最后还要将这个
ID
设置为浏览器可以保存的
cookie
值。
URL
,包含
login.jsp
。身份服务是用来保护其它应用服务的,用户一般在訪问一个受
SSOAuth
保护的
Web
应用的某个
URL
时,当前这个应用会发现当前的用户还没有登录,便强制将也页面转向
SSOAuth
的
login.jsp
,让用户登录。假设登录成功后,应该自己主动的将用户的浏览器指向用户最初想訪问的那个
URL
。在
handlerFromLogin()
这种方法中,我们通过接收
“
goto”
这个參数来保存用户最初訪问的
URL
,成功后便又一次定向到这个页面中。
cookie
的时候,我使用了一个setMaxAge(6000)
的方法。这种方法是用来设置
cookie
的有效期,单位是秒。假设不使用这种方法或者參数为负数的话,当浏览器关闭的时候,这个
cookie
就失效了。在这里我给了非常大的值(
1000
分钟),导致的行为是:当你关闭浏览器(或者关机),下次再打开浏览器訪问刚才的应用,仅仅要在
1000
分钟之内,就不须要再登录了。我这样做是以下要介绍的桌面
SSO
中所须要的功能。
具有
SSO
功能的
web
应用源码解析
WEB-SSO
的功能,仅仅有身份认证服务是不够的。这点非常显然,要想使多个应用具有单点登录的功能,还须要每一个应用本身的配合:将自己的身份认证的服务交给一个统一的身份认证服务-
SSOAuth
。
SSOAuth
服务中提供的各个方法就是供每一个添�
SSO
的
Web
应用来调用的。
Web
应用须要
SSO
的功能,应该通过下面的交互过程来调用身份认证服务的提供的认证服务:
- Web应用中每个须要安全保护的URL在訪问曾经,都须要进行安全检查,假设发现没有登录(没有发现认证之后所带的cookie),就又一次定向到SSOAuth中的login.jsp进行登录。
- 登录成功后,系统会自己主动给你的浏览器设置cookie,证明你已经登录过了。
- 当你再訪问这个应用的须要保护的URL的时候,系统还是要进行安全检查的,可是这次系统可以发现对应的cookie。
- 有了这个cookie,还不能证明你就一定有权限訪问。由于有可能你已经logout,或者cookie已经过期了,或者身份认证服务重起过,这些情况下,你的cookie都可能无效。应用系统拿到这个cookie,还须要调用身份认证的服务,来推断cookie时候真的有效,以及当前的cookie相应的用户是谁。
- 假设cookie效验成功,就同意用户訪问当前请求的资源。
- 在每一个被訪问的资源中(JSP或Servlet)中都添�身份认证的服务,来获得cookie,而且推断当前用户是否登录过。只是这个笨方法没有人会用:-)。
- 能够通过一个controller,将全部的功能都写到一个servlet中,然后在URL映射的时候,映射到全部须要保护的URL集合中(比如*.jsp,/security/*等)。这种方法能够使用,只是,它的缺点是不能重用。在每一个应用中都要部署一个同样的servlet。
- Filter是比較好的方法。符合Servlet2.3以上的J2EE容器就具有部署filter的功能。(Filter的使用能够參考JavaWolrd的文章http://www.javaworld.com/javaworld/jw-06-2001/jw-0622-filters.html)Filter是一个具有非常好的模块化,可重用的编程API,用在SSO正合适只是。本例子就是使用一个filter来完毕以上的功能。
package SSO; import java.io.*; import java.net.*; import java.util.*; import java.text.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.*; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.GetMethod; public class SSOFilter implements Filter { private FilterConfig filterConfig = null; private String cookieName=”WangYuDesktopSSOID”; private String SSOServiceURL= “http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth”; private String SSOLoginPage= “http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp”; public void init(FilterConfig filterConfig) { this.filterConfig = filterConfig; if (filterConfig != null) { if (debug) { log(“SSOFilter:Initializing filter”); } } cookieName = filterConfig.getInitParameter(“cookieName”); SSOServiceURL = filterConfig.getInitParameter(“SSOServiceURL”); SSOLoginPage = filterConfig.getInitParameter(“SSOLoginPage”); } ….. ….. } |
SSOServiceURL
和
SSOLoginPage
。由于当前的
Web
应用非常可能和身份认证服务(
SSOAuth
)不在同一台机器上,所以须要让这个
filter
知道身份认证服务部署的
URL
,这样才干去调用它的服务。另外一点就是由于身份认证的服务调用是要通过
http
协议来调用的(在本例子中是这样设计的,读者全然能够设计自己的身份服务,使用别的调用协议,如
RMI
或
SOAP
等等),全部笔者引用了
apache
的
commons
工具包(具体信息情訪问
apache
的站点
http://jakarta.apache.org/commons/index.html
),当中的
“
httpclient”
能够大大简化
http
调用的编程。
filter
的主体方法
doFilter():
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (debug) log(“SSOFilter:doFilter()”); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String result=”failed”; String url = request.getRequestURL().toString(); String qstring = request.getQueryString(); if (qstring == null) qstring =””; // 检查 http 请求的 head 是否有须要的 cookie String cookieValue =””; javax.servlet.http.Cookie[] diskCookies = request.getCookies(); if (diskCookies != null) { for (int i = 0; i < diskCookies.length; i++) { if(diskCookies[i].getName().equals(cookieName)){ cookieValue = diskCookies[i].getValue(); // 假设找到了对应的 cookie 则效验其有效性 result = SSOService(cookieValue); if (debug) log(“found cookies!”); } } } if (result.equals(“failed”)) { // 效验失败或没有找到 cookie ,则须要登录 response.sendRedirect(SSOLoginPage+”?goto=”+url); } else if (qstring.indexOf(“logout”) > 1) {//logout 服务 if (debug) log(“logout action!”); logoutService(cookieValue); response.sendRedirect(SSOLoginPage+”?goto=”+url); } else {// 效验成功 request.setAttribute(“SSOUser”,result); Throwable problem = null; try { chain.doFilter(req, res); } catch(Throwable t) { problem = t; t.printStackTrace(); } if (problem != null) { if (problem instanceof ServletException) throw (ServletException)problem; if (problem instanceof IOException) throw (IOException)problem; sendProcessingError(problem, res); } } } |
方法的逻辑也是很easy的,在接收到请求的时候,先去查找是否存在期望的
cookie
值,假设找到了,就会调用
SSOService(cookieValue)
去效验这个
cookie
的有效性。假设
cookie
效验不成功或者
cookie
根本不存在,就会直接转到登录界面让用户登录;假设
cookie
效验成功,就不会做不论什么阻拦,让此请求进行下去。在配置文件里,有以下的一个节点表示了此
filter
的
URL
映射关系:仅仅拦截全部的
jsp
请求。
<filter-name>SSOFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
private String SSOService(String cookievalue) throws IOException { String authAction = “?action=authcookie&cookiename=”; HttpClient httpclient = new HttpClient(); GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue); try { httpclient.executeMethod(httpget); String result = httpget.getResponseBodyAsString(); return result; } finally { httpget.releaseConnection(); } } private void logoutService(String cookievalue) throws IOException { String authAction = “?action=logout&cookiename=”; HttpClient httpclient = new HttpClient(); GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue); try { httpclient.executeMethod(httpget); httpget.getResponseBodyAsString(); } finally { httpget.releaseConnection(); } } |
apache
中的
httpclient
訪问
SSOAuth
提供的认证服务来完毕效验
cookie
和
logout
的功能。
IDE
(
NetBeans
)替我自己主动生成的。
当前方案的安全局限性
WEB-SSO
的方案是一个比較简单的雏形,主要是用来演示
SSO
的概念和说明
SSO
技术的实现方式。有非常多方面还须要完好,当中安全性是非常重要的一个方面。
SSO
技术的主要目的之中的一个就是加强安全性,减少安全风险。由于採用了
SSO
,在网络上传递password的次数减少,风险减少是显然的,可是当前的方案却有其它的安全风险。由于
cookie
是一个用户登录的唯一凭据,对
cookie
的保护措施是系统安全的重要环节:
- cookie的长度和复杂度
在本方案中,cookie是有一个固定的字符串(我的姓名)加上当前的时间戳。这种cookie非常easy被伪造和推測。怀有恶意的用户假设推測到合法的cookie就能够被当作已经登录的用户,随意訪问权限范围内的资源 - cookie的效验和保护
在本方案中,尽管password仅仅要传输一次就够了,可cookie在网络中是常常传来传去。一些网络探測工具(如sniff, snoop,tcpdump等)能够非常easy捕获到cookie的数值。在本方案中,并没有考虑cookie在传输时候的保护。另外对cookie的效验也过于简单,并不去检查发送cookie的来源到底是不是cookie最初的拥有者,也就是说无法区分正常的用户和仿造cookie的用户。 - 当当中一个应用的安全性不好,其它全部的应用都会受到安全威胁
由于有SSO,所以当某个处于 SSO的应用被黒客攻破,那么非常easy攻破其它处于同一个SSO保护的应用。
SSO
解决方式中都会有所考虑,提供相关的安全措施和保护手段,比如
Sun
公司的
Access Manager
,
cookie
的复杂读和对
cookie
的保护都做得很好。另外在
OpneSSO
(
https://opensso.dev.java.net
)的架构指南中也给出了部分安全措施的解决方式。
当前方案的功能和性能局限性
- 当前所提供的登录认证模式仅仅有一种:username和password,并且为了简单,将username和password放在内存其中。其实,用户身份信息的来源应该是多种多样的,能够是来自数据库中,LDAP中,甚至于来自操作系统自身的用户列表。还有非常多其它的认证模式都是商务应用必不可少的,因此SSO的解决方式应该包含各种认证的模式,包含数字证书,Radius, SafeWord ,MemberShip,SecurID等多种方式。最为灵活的方式应该同意可插入的JAAS框架来扩展身份认证的接口
- 我们编写的Filter仅仅能用于J2EE的应用,而对于大量非Java的Web应用,却无法提供SSO服务。
- 在将Filter应用到Web应用的时候,须要对容器上的每个应用都要做对应的改动,又一次部署。而更加流行的做法是Agent机制:为每个应用server安装一个agent,就能够将SSO功能应用到这个应用server中的全部应用。
- 当前的方案不能支持分别位于不同domain的Web应用进行SSO。这是由于浏览器在訪问Webserver的时候,只会带上和当前webserver具有同样domain名称的那些cookie。要提供跨域的SSO的解决方式有非常多其它的方法,在这里就不多说了。Sun的Access Manager就具有跨域的SSO的功能。
- 另外,Filter的性能问题也是须要重视的方面。由于Filter会截获每个符合URL映射规则的请求,获得cookie,验证其有效性。这一系列任务是比較消耗资源的,特别是验证cookie有效性是一个远程的http的调用,来訪问SSOAuth的认证服务,有一定的延时。因此在性能上须要做进一步的提高。比如在本例子中,假设将URL映射从“.jsp”改成“/*”,也就是说filter对全部的请求都起作用,整个应用会变得非常慢。这是由于,页面其中包括了各种静态元素如gif图片,css样式文件,和其它html静态页面,这些页面的訪问都要通过filter去验证。而其实,这些静态元素没有什么安全上的需求,应该在filter中进行推断,不去效验这些请求,性能会好非常多。另外,假设在filter中加上一定的cache,而不须要每个cookie效验请求都去远端的身份认证服务中运行,性能也能大幅度提高。
- 另外系统还须要非常多其它的服务,如在内存中定时删除没用的cookie映射等等,都是一个严肃的解决方式须要考虑的问题。
桌面
SSO
的实现
WEB-SSO
的概念延伸开,我们能够把
SSO
的技术拓展到整个桌面的应用,不只局限在浏览器。
SSO
的概念和原则都没有改变,只须要再做一点点的工作,就能够完毕桌面
SSO
的应用。
SSO
和
WEB-SSO
一样,关键的技术也在于怎样在用户登录过后保存登录的凭据。在
WEB-SSO
中,登录的凭据是靠浏览器的
cookie
机制来完毕的;在桌面应用中,能够将登录的凭证保存到不论什么地方,仅仅要全部
SSO
的桌面应用都共享这个凭证。
SSO
的例子
(http://gceclub.sun.com.cn/wangyu/desktop-sso/desktopsso.zip)
和所有源代码(
http://gceclub.sun.com.cn/wangyu/desktop-sso/desktopsso_src.zip
),尽管简单,可是它具有桌面
SSO
大多数的功能,略微加以扩充就能够成为自己的解决方式。
桌面例子的部署
- 执行此桌面SSO须要三个前提条件:
a) WEB-SSO的身份认证应用应该正在执行,由于我们在桌面SSO其中须要用到统一的认证服务
b) 当前桌面须要执行Mozilla或Netscape浏览器,由于我们将ticket保存到mozilla的cookie文件里
c) 必须在JDK1.4以上执行。(WEB-SSO须要JDK1.5以上) - 解开desktopsso.zip文件,里面有两个文件夹bin和lib。
- bin文件夹下有一些脚本文件和配置文件,当中config.properties包括了三个须要配置的參数:
a) SSOServiceURL要指向WebSSO部署的身份认证的URL
b) SSOLoginPage要指向WebSSO部署的身份认证的登录页面URL
c) cookiefilepath要指向当前用户的mozilla所存放cookie的文件 - 在bin文件夹下另一个login.conf是用来配置JAAS登录模块,本例子提供了两个,读者能够随意选择当中一个(也能够都选),再又一次执行程序,查看登录认证的变化
- 在bin下的执行脚本可能须要作对应的改动
a) 假设是在unix下,各个jar文件须要用“:”来隔开,而不是“;”
b) java 执行程序须要放置在当前执行的路径下,否则须要加上java的路径全名。
桌面例子的执行
Java
控制台程序,这三个程序单独执行都须要登录。假设执行第一个命叫“
GameSystem
”
的程序,提示须要输入username和password:
这时候再执行第二个桌面
Java
应用(
mailSystem
)的时候,就不须要再登录了,直接就显示出来刚才登录的用户。
logout
,执行它之后,用户便退出系统。再訪问的时候,又须要又一次登录了。请读者再制裁执行完
logout
之后,又一次验证一下前两个应用的
SSO
:先执行第二个应用,再执行第一个,会看到同样的效果。
Java
应用之间
SSO
,还能和浏览器进行
SSO
,也就是将浏览器也当成是桌面的一部分。这对一些行业有着不小的吸引力。
Mozilla
浏览器,訪问曾经提到的那两个
WEB
应用,会发现仅仅要桌面应用假设登录过,
Web
应用就不用再登录了,并且能显示刚才登录的用户的信息。读者能够在几个桌面和
Web
应用之间进行登录和
logout
的试验,看看它们之间的
SSO
。
桌面例子的源代码分析
SSO
的例子使用了
JAAS
(要了解
JAAS
的具体的信息请參考
http://java.sun.com/products/jaas
)。
JAAS
是对
PAM
(
Pluggable Authentication Module
)的
Java
实现,来完毕
Java
应用可插拔的安全认证模块。使用
JAAS
作为
Java
应用的安全认证模块有非常多优点,最基本的是不须要改动源码就能够更换认证方式。比如原有的
Java
应用假设使用
JAAS
的认证,假设须要应用
SSO
,仅仅须要改动
JAAS
的配置文件即可了。如今在流行的
J2EE
和其它
Java
的产品中,用户的身份认证都是通过
JAAS
来完毕的。在例子中,我们就展示了这个功能。请看配置文件
login.conf
DesktopSSO { desktopsso.share.PasswordLoginModule required; desktopsso.share.DesktopSSOLoginModule required; }; |
test
用户(password是
12345
)才干登录。当我们注解掉第一个模块的时候,仅仅有第二个模块起作用,桌面
SSO
才会起作用。
Java
桌面例子程序都是标准
JAAS
应用,熟悉
JAAS
的程序猿会非常快了解。
JAAS
中基本的是登录模块(
LoginModule
)。以下是
SSO
登录模块的源代码:
public class DesktopSSOLoginModule implements LoginModule { ………. private String SSOServiceURL = “”; private String SSOLoginPage = “”; private static String cookiefilepath = “”; ……… |
config.properties
的文件里,我们配置了它们的值:
SSOServiceURL=http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth SSOLoginPage=http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp cookiefilepath=C://Documents and Settings//yw137672//Application Data//Mozilla//Profiles//default//hog6z1ji.slt//cookies.txt |
和
SSOLoginPage
成员变量指向了在
Web-SSO
中用过的身份认证模块:
SSOAuth
,这就说明在桌面系统中我们试图和
Web
应用共用一个认证服务。而
cookiefilepath
成员变量则泄露了一个“天机”:我们使用了
Mozilla
浏览器的
cookie
文件来保存登录的凭证。换句话说,和
Mozilla
共用了一个保存登录凭证的机制。之所以用
Mozilla
是应为它的
Cookie
文件格式简单,非常easy编程訪问和改动随意的
Cookie
值。(我试图解析
Internet Explorer
的
cookie
文件但没有成功。)
login()
方法。逻辑也是很easy:先用
Cookie
来登陆,假设成功,则直接就进入系统,否则须要用户输入username和password来登录系统。
public boolean login() throws LoginException{ try { if (Cookielogin()) return true; } catch (IOException ex) { ex.printStackTrace(); } if (passwordlogin()) return true; throw new FailedLoginException(); } |
方法的实体,它的逻辑是:
先从
Cookie
文件里获得对应的
Cookie
值,通过身份效验服务效验
Cookie
的有效性。假设
cookie
有效
就算登录成功;假设不成功或
Cookie
不存在,用
cookie
登录就算失败。
public boolean Cookielogin() throws LoginException,IOException { String cookieValue=””; int cookieIndex =foundCookie(); if (cookieIndex<0) return false; else cookieValue = getCookieValue(cookieIndex); username = cookieAuth(cookieValue); if (! username.equals(“failed”)) { loginSuccess = true; return true; } return false; } |
Callback
的机制和屏幕输入输出进行信息交互,完毕用户登录信息的获取;获取信息以后通过
userAuth
方法来调用远端
SSOAuth
的服务来判定当前登录的有效性。
public boolean passwordlogin() throws LoginException { // // Since we need input from a user, we need a callback handler if (callbackHandler == null) { throw new LoginException(“No CallbackHandler defined”); } Callback[] callbacks = new Callback[2]; callbacks[0] = new NameCallback(“Username”); callbacks[1] = new PasswordCallback(“Password”, false); // // Call the callback handler to get the username and password try { callbackHandler.handle(callbacks); username = ((NameCallback)callbacks[0]).getName(); char[] temp = ((PasswordCallback)callbacks[1]).getPassword(); password = new char[temp.length]; System.arraycopy(temp, 0, password, 0, temp.length); ((PasswordCallback)callbacks[1]).clearPassword(); } catch (IOException ioe) { throw new LoginException(ioe.toString()); } catch (UnsupportedCallbackException uce) { throw new LoginException(uce.toString()); } System.out.println(); String authresult =””; try { authresult = userAuth(username, password); } catch (IOException ex) { ex.printStackTrace(); } if (! authresult.equals(“failed”)) { loginSuccess= true; clearPassword(); try { updateCookie(authresult); } catch (IOException ex) { ex.printStackTrace(); } return true; } loginSuccess = false; username = null; clearPassword(); System.out.println( “Login: PasswordLoginModule FAIL” ); throw new FailedLoginException(); } |
和
userAuth
方法都是利用
apahce
的
httpclient
工具包和远程的
SSOAuth
进行
http
连接,获取服务。
private String cookieAuth(String cookievalue) throws IOException{ String result = “failed”; HttpClient httpclient = new HttpClient(); GetMethod httpget = new GetMethod(SSOServiceURL+Action1+cookievalue); try { httpclient.executeMethod(httpget); result = httpget.getResponseBodyAsString(); } finally { httpget.releaseConnection(); } return result; } private String userAuth(String username, char[] password) throws IOException{ String result = “failed”; String passwd= new String(password); HttpClient httpclient = new HttpClient(); GetMethod httpget = new GetMethod(SSOServiceURL+Action2+username+”&password=”+passwd); passwd = null; try { httpclient.executeMethod(httpget); result = httpget.getResponseBodyAsString(); } finally { httpget.releaseConnection(); } return result; } |
http://java.sun.com/developer/technicalArticles/Security/pwordmask/
真正安全的全方位
SSO
解决方式:
Kerberos
SSO
和
WEB-SSO
)都有一个共性:要想将一个应用集成到我们的
SSO
解决方式中,或多或少的须要改动应用程序。
Web
应用须要配置一个我们预制的
filter
;桌面应用须要加上我们桌面
SSO
的
JAAS
模块(至少要改动
JAAS
的配置文件)。但是有非常多程序是没有源码和无法改动的,比如经常使用的远程通讯程序
telnet
和
ftp
等等一些操作系统自己带的经常使用的应用程序。这些程序是非常难改动添�到我们的
SSO
的解决方式中。
SSO
解决方式能够解决这些问题,这就是
Kerberos
协议(
RFC 1510
)。
Kerberos
是网络安全应用标准
(http://web.mit.edu/kerberos/)
,由
MIT
学校发明,被主流的操作系统所採用。在採用
kerberos
的平台中,登录和认证是由操作系统本身来维护,认证的凭证也由操作系统来保存,这样整个桌面都能够处于同一个
SSO
的系统保护中。操作系统中的各个应用(如
ftp,telnet
)仅仅须要通过配置就能添�到
SSO
中。另外使用
Kerberos
最大的优点在于它的安全性。通过密钥算法的保证和密钥中心的建立,能够做到用户的password根本不须要在网络中传输,而传输的信息也会十分的安全。
Kerberos
的操作系统包含
Solaris, windows,Linux
等等主流的平台。仅仅只是要搭建一个
Kerberos
的环境比較复杂,
KDC
(密钥分发中心)的建立也须要相当的步骤。
Kerberos
拥有很成熟的
API
,包含
Java
的
API
。使用
Java Generic Security Services(GSS) API
而且使用
JAAS
中对
Kerberos
的支持(具体信息请參见
Sun
的
Java&Kerberos
教程
http://java.sun.com/ j2se/1.5.0/docs/guide/security/jgss/tutorials/index.html
),要将我们这个例子改造成对
Kerberos
的支持也是不难的。 值得一提的是在
JDK6.0
(
http://www.java.net/download/jdk6
)其中直接就包含了对
GSS
的支持,不须要单独下载
GSS
的包。
总结
SSO
的基本原理,并提供了一种实现的方式。通过对源码的分析来掌握开发
SSO
服务的技术要点和充分理解
SSO
的应用范围。可是,本文只说明了身份认证的服务,而另外一个和身份认证密不可分的服务
—-
权限效验,却没有提到。要开发出真正的
SSO
的产品,在功能上、性能上和安全上都必须有更加完备的考虑。
Sun
中国project研究院的
Java
project师,如今的主要负责全球合作伙伴的技术支持。作为一名
Java
资深project师和架构师,王昱在
Java
的非常多领域都有多年的造诣,特别是在
Java
虚拟机、
J2EE
技术
(
包含
EJB, JSP/Servlet, JMS
和
Web services
等技术
)
、集群技术和
Java
应用性能调优上有着较为丰富的经验。以前多次在重要的
Java
会议发表演讲,并在国际著名的
Java
技术站 点发表文章。
- OpenSSO 的站点: https://opensso.dev.java.net
- 在java应用中使用掩码的password提示:http://java.sun.com/developer/technicalArticles/Security/pwordmask/
- Kerberos的资源站点:http://web.mit.edu/kerberos/
- Sun的Java&Kerberos教程:http://java.sun.com/j2se/1.5.0/docs/guide/security/jgss/tutorials/index.html
- Apache社区提供的HttpClient工具包网址:http://jakarta.apache.org/commons/index.html)
- Filter的使用教程文章:http://www.javaworld.com/javaworld/jw-06-2001/jw-0622-filters.html
- 我的博客http://yuwang881.blog.sohu.com/3184816.html
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/118157.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...