部署集成 >> 企业级集成方案 >> 单点登录

我是社区第54755位番薯,欢迎点我头像关注我哦~
本帖最后由 加菲喵 于 2015-9-10 15:36 编辑

单点登陆
一、平台的权限、单点登录
Session和Cookie:
Session是服务器和客户端的会话。客户端浏览器访问服务器,两者之间就建立了一种关系,服务器端保存这种关系的是Session(Servlet中他对应的类是javax.servlet.http.HttpSession),客户端也保存了这种关系放在了Cookie里,对应的名字是JSESSIONID。

举例操作:
A、启动fr的内置jetty服务器(单纯的启动,不要访问任何东西),访问http://localhost:8075/WebReport,查看请求的header:
1339955cc00f8b4ffd.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png

B、访问http://localhost:8075/WebReport/ReportServer,查看请求header:
994355cc0109a985e.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png

C、访问http://localhost:8075/WebReport/ReportServer?op=fs,他会自动跳转到地址http://localhost:8075/WebReport/ReportServer?op=fs_load&cmd=fs_signin,再看header:
8067355cc0120ad823.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png

D、登入fs,查看header:
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png
7182655cc012c4db6d.png
E、重启服务器,启动jetty服务器(注意还是只启动服务器不浏览东西),浏览器访问http://localhost:8075/WebReport/,再看header:
6577155cc013bc24d5.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png

F、重启浏览器,再访问http://localhost:8075/WebReport/,查看请求的header:
1300055cc014973106.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png

服务器端Session的生成时间:一个http请求,当服务器端生成了Session对象的时候,就会给前端浏览器传递一个JSESSIONID的Cookie,这个Cookie是会话Cookie,跟持久化的Cookie是不同的。根据A和B得到的结果,可以判断并不是客户端一访问就生成Session的,事实上是,只有当服务器端调用req.getSession(true)这样的语句时才会被创建(其中req是HttpServletRequest对象实例)。
这里说一下req.getSession(true)和req.getSession(false)的区别,如果当前客户端和服务器的会话对象没有时,调用方法req.getSession(true)会自动创建一个会话对象,调用方法req.getSession(false)不会创建一个会话对象,而是返回null,默认情况下,调用req.getSession()的时候等效于req.getSession(true),也就是会自动创建一个会话对象。当然客户端和服务器端会话存在的话这三种参数的情况就没有区别了。
这里的A和B都没有生成服务器端的Session,但是这俩流程上是不同的,A访问WebReport这个app,并没有走Servlet,而B访问的是WebReport这个app中的ReportServer这个Servlet,这时候才走FR的代码,才有可能出现req.getSession(true)这种语句而创建Session。B这一步有可能创建但是没有创建,只是单纯的展现一个类似于欢迎界面的东西,不需要对权限做任何的判断和操作。
到了C访问op=fs这一步就需要获取Session来判断这个客户端和服务器端到底有没有会话了,req.getSession(true)这一步就是必需的了,如果有会话并且里面记录了对应的权限的话,那么就直接进入fs操作界面了,否则要跳转到登录界面的。可以看到C操作对应的Cookie里面有JSESSIONID,他是由服务器端创建服务器端Session的时候生成的客户端的会话Cookie。
到D这步,登录进fs,还是有JSESSIONID的,而且跟之前的值是一样的。
到E,重启服务器,再次访问WebReport这个application的根目录,发现JSESSIONID仍然存在而且值不变。这里得说一下服务器端Session的销毁时间,服务器的Session是存储在内存的,服务器关掉后,Session的信息也就清空了(但是有的web服务器是可以做Session持久化的比如tomcat,服务器关闭的时候把Session的信息存储在硬盘上,启动的时候再从硬盘读取到内存),Jetty重启了之后客户端的Cookie里的JSESSIONID仍然存在,这里就涉及客户端会话Cookie的销毁时间了,客户端会话Cookie的生成是由服务器端决定的,然而销毁并不是由服务器端决定的,而是客户端自己决定的,在关闭浏览器的时候销毁JSESSIONID。这个其实也是很好理解的,现在的http请求都是客户端发起请求--服务器端相应请求,服务器是没法主动向客户端推销东西的,所以服务器端关闭的时候没法告知客户端清掉JSESSIONID,当客户端浏览器已经关闭,表示这个会话已经结束了,客户端的会话Cookie在这个时候就应该清除掉了,这也就是会话Cookie和持久Cookie的不同,有的浏览器可以记住用户名密码,重新启动浏览器再访问的时候能够实现自动登录,保存这个的Cookie就是持久的Cookie,存储在硬盘里。
到F,这个结果是验证了上面说的客户端会话Cookie的销毁时间是在客户端浏览器关闭的时候。当然后台服务器也是可以设置Session的超时时间的,超过这个时间Session也可以被服务器销毁。

上面说了用于权限的Session和Cookie的相关东西,再看看FR当中具体是怎么做的。
权限首先想到的应该是登录界面的登录操作对应的后台Action,代码里这个Action是FS模块中的FSLoadLoginAction。7.1之前的版本是有管理平台的(op=fr_platform),管理平台Platform和数据决策系统FS使用的是不同的权限系统,管理平台的登录处理类是LoginAction。上面说到Servlet后台Session对应的类是HttpSession,HttpSession可以保存一些信息或者说属性,方法是setAttribute(java.lang.Strings, java.lang.Object o),所谓的登录其实就是往HttpSession对象里设置指定的属性名(参数s)和属性值(参数o),而所谓的权限验证就是取HttpSession里指定的属性名对应的值来判断。
看一下FSLoadLoginAction的代码就会发现登录处理的语句:
HttpSession session = req.getSession(true);
PrivilegeInfoSessionMananger.login(new FServicePrivilegeLoader(username,UserControl.getInstance().getAllSRoleNames(userid),UserControl.getInstance().getUserDP(userid)), session, res);
session.setAttribute(FSConstants.P_KEYS.PRIVILEGE_AUTHENCATION_KEY,authentication);
UserControl.getInstance().login(userid);
通过客户端登录页面输入的东西(用户名和密码),结合服务器设置的权限验证方式(4种方式),生成一个authentication对象,放到HttpSession里面,这个对象对应的属性名是FSConstants.P_KEYS.PRIVILEGE_AUTHENCATION_KEY,查看具体位置是这么个字符串“fr_fs_auth_key”。这之后这个客户端再访问一些设置了权限的东西的时候,就会拿设置的这个属性来判断了。对于7.1之前的平台管理的登录可以看一下LoginAction的相关代码,会发现它对应的属性名是Constants.P.PRIVILEGE_AUTHENCATION_KEY,也就是“fr_authentication_key”,当然,对应的authentication对象也是不一样的。用过7.1之前版本的话可能会发现一个现象,就是登录了FS,再登录Platform,再转到FS的页面发现FS的权限失效了,同样的登录了Platform再登录FS也会发现Platform的权限失效,这是因为两套权限系统在往HttpSession里面设置authentication的时候,都把HttpSession里保存的其他的权限属性给清除了。

再看一下单点登录,单点登录说白了就一句话,在登录其他系统的同时登录fr的权限系统,这样就不会从客户自己已登录的系统跳到fr的系统(fs)的时候显示没权限或者跳转到登录界面再次登录。
看一个自动登录的例子:
<html>
<head>
<script type="text/javascript" src="http://114.215.175.35:8080/WebReport/ReportServer?op=emb&resource=finereport.js&inter=zh_CN"></script>
</head>
<body>
<script>
$.ajax({
url: "http://114.215.175.35:8080/WebReport/ReportServer?op=fs_load&cmd=sso",
dataType: "jsonp",
data: {
username: "username",
password: "password"
},
success: function(data) {
if (data.status == "success") {
window.location.href = "http://114.215.175.35:8080/WebReport/ReportServer?op=fs";
} else {
alert("failed");
}
},
error: function() {
alert("error");
}
});
</script>
</body>
</html>
把上面的代码复制到一个html文件里,并把第10行data里的username和password改成fr的CRM(bugtracker)用户名和密码,就可以实现自动登录。
这个东西是没有部署在任何web服务器上的html页面,是本地文件,这里就涉及跨域的问题,当然客户那里一般是部署在web服务器上的页面上使用的,但是也是很有可能跨域的。客户集成起来到底跨不跨域无法预知,于是必须提供跨域的单点登录方案。Jsonp就是ajax跨域请求的处理方式,具体的详细说明自己搜搜看,fr的离线填报也是用的jsonp的方式实现跨域请求的。
看请求op=fs_load&cmd=sso,fs_load对应的是平台的登录服务相关类FSLoadService,sso对应的是单点登录处理类FSSingleSignOnAction,可以看到这个类是平台登录处理类FSLoadLoginAction的子类,具体的实现都是通过FSLoadLoginAction这个类的方法来完成的,也就是说后台跟fr内置的登录类是一样的处理,只是前台怎么把用户名和密码这个信息传到后台的方式不一样而已。

二、权限数据的存储
具体的权限分配,至少要记录以下的东西:
用户信息
角色信息
模板信息
角色对应的权限信息

具体用户的对模板的操作权限信息是通过角色来实现的。FR的角色分为两种,一种是公司部门角色(由部门和职位组成),一种是普通角色(自定义角色)。权限的分配是取两者设置的并集。

关于用户和角色信息的采集,FR提供了两种方式,一种是设置同步数据集,从客户自己定义的数据库中读取用户名、密码、部门、职位、角色等信息,一种是不开启同步数据集,使用内置的hsql数据库中的表。

开启同步数据集方式:
在一个数据集里记录用户信息和角色信息。
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image007.png


不开启同步数据集方式:
Hsql是一个轻量级的开源java数据库,fs的相关的东西都放在finedb这个数据库中,当然,目前来说,应该是除了日志以外的东西都放在finedb中,日志的放在logdb中。使用hsql的时候上面的一组信息就是放在不同的表里了。用户的相关信息放在表FR_T_USER,如图:
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image008.png

对于角色,有如下表:
部门表:
FR_T_DEPARTMENT
职位表:
FR_T_POST

部门角色表:
FR_T_COMPANYROLE
自定义角色表:
FR_T_CUSTOMROLE

部门角色对应的用户表:
FR_T_DEPARTMENT_POST_USER
自定义角色对应的用户表:
FR_T_CUSTOMROLE_USER

同步数据集里面存放了用户和角色的信息,但是里面目前并没有配置具体模板或者模块的权限,这个东西还是记录在hsql里面的:
部门角色的预览模板的权限表:
FR_T_COMPANYROLEENTRYPRIVILEGE
自定义角色预览模板的权限表:
FR_T_CUSTOMROLEENTRYPRIVILEGE

部门角色预览平台管理模块的权限表:
FR_T_COMPANYROLEMODULEPRIVILEGE
自定义角色预览平台管理模块的权限表:
FR_T_CUSTOMROLEMODULEPRIVILEGE

三、接口
超级管理员和普通用户是不一样的,超管没有角色信息,但是有名字,也有id,之前提到他的相关信息存放在PrivilegeManager类里。
获取管理员id:
PrivilegeManager.SYSADMINID
获取管理员名字:
PrivilegeManager.getInstance().getRootManagerName()
获取管理员密码:
PrivilegeManager.getInstance().getRootManagerPassword()
获取认证方式:
PrivilegeManager.getInstance().getAuthenticationProvider()

FS相关配置放在FSConfig里面,简单举例几个成员。
private Control control = HSQLDBDAOControl.getInstance();这个control是控制用户和角色的来源,也就是是否开启同步数据集。默认值的这个HSQLDBDAOControl.getInstance()就是不开启同步数据集使用hsql数据库的情况,开关同步数据集是通过setControl方法来改变control的值,开启同步数据集的话这个control就是TableDataDAOControl.getInstance()。之所以不用Boolean类型是为后面增加方式提供方便。
homePageURL:主页url
loginImageID4FS:登陆图片
bgImageID4FS:背景图片
authenticateType:验证方式。他的值有default:默认方式(用户名密码)、ldap:LDAP验证、http:HTTP验证。
loginAction:登录的Action,默认值就是上面提到的FSLoadLoginAction。
他们都是属性,获取和设置的方法满足默认的get和set命名规则。

FS的权限相关的东西都是在fservice模块的,这个模块其他的模块是取不到的,有些功能是需要读取fs的权限设置的比如Excel批量提交和数据上报,然而这俩却被安排在不依赖于fservice的引擎模块,所以开了些常用的方法在FSBridge这个类里。
获取当前用户id
FSBridge.getCurrentUserID(req)
获取用户所在的所有部门角色id数组
FSBridge.getCompanyRolesByUserId(long userId)
获取用户所在的所有自定义角色id数组
FSBridge.getCustomRolesByUserId(userId)
获取部门角色里面的用户id数组
FSBridge.getUsersByCompanyRoleId(roleId)
获取自定义角色里面的用户id数组
FSBridge.getUsersByCustomRoleId(roleId)
根据id读取用户的信息
FSBridge.getUserInfoById(userId)
根据id获取部门角色信息
FSBridge.getCompanyRoleInfoById(roleId)
根据id获取普通角色信息
FSBridge.getCustomRoleInfoById(roleId)


参与人数 +2 F豆 +1 F币 +300 理由
qzlf + 1 很给力!
传说哥 + 300 很给力!

查看全部评分

发表于 2015-8-13 10:31:18
评分:
难度:25
详尽度: 20
通用度:8
建议:
1、详尽度是够了,部分代码需要备注说明
2、文档格式不对,需要修改成帮助文档格式,并划分归类
3、最好附上图片,使用户理解起来更加容易
发表于 2015-8-14 13:49:11
难度25
详尽度30
通用度10分

这个是帮助文档,这么多显得有点太大篇幅了,很少有人会去看完的,可以借鉴下原帮助文档的单点登录的写法
发表于 2015-8-25 17:34:53
8785155dc36bb7ad96.png
发表于 2015-9-2 15:23:12
3328355e6a3de39876.png
发表于 2015-12-23 10:08:08
辛苦了,感谢
发表于 2016-5-4 09:17:19
收藏!学习!
发表于 2018-3-22 10:35:06
您好 我想跟您请教一下php与报表做sso怎么实现 用session做关联的 您如果有好的文章可以推荐给我一下吗 或者指点我一下 非常感谢您 这对我很重要 拜托您了 这是公司上个做报表同事的账号 我不太知道这边的规矩 就是需要有f逗什么的您也可以直接跟我说 我不太会说话 请您别介意
发表于 2018-9-12 16:14:16
为什么看不到内容了~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

8回帖数 1关注人数 6896浏览人数
最后回复于:2018-9-12 16:14

任务进行中

    返回顶部 返回列表