@iamfox
2015-05-06T18:03:21.000000Z
字数 5852
阅读 1768
方案
单点登录需求为在两个web系统间实现统一的注册和统一的登录效果,具体情况分为跨域和不跨域两种。
在不跨域情况下,如果各子系统session相通,则无必要采用单点登录方案,可以继续使用传统session登录状态判断。
若session各子系统独立,而cookie可以互通,即每个web系统都可以读取到共同的cookie,则采用本方案,通过可重用的jar包,为每个web系统模块提供统一注册和登录功能。
本方案另一个前提是各子系统拥有一个共同访问的数据源,以保存用户信息。如果数据源也完全隔离,可采用跨域方案。
基本业务实体如下:
public class SSOUser {
/**
* 注册时创建一个新用户
*/
private String id;
/**
* 用户名
*/
private String userName;
/**
* 密文密码
*/
private String encryPassword;
/**
* 最后登录时间
*/
private Date lastLoginDate;
/**
* 当前登录令牌
*/
private String currentLoginToken;
/**
* 该用户的注册来源子系统,可选
*/
private String registerFromClient;
/**
* 最后一次登录来源子系统,可选
*/
private String lastLoginFromClient;
...
}
jar包中提供了一个用户操作接口:
public interface SSOUserService {
/**
* 注册时创建一个新用户,ssoUser持久化
* 可选参数为是否允许用户名重复,true为可重复
*/
public void createUser(SSOUser, Boolean allowUserNameRepeat);
/**
* 修改用户名
*/
public void changeUserName(String ssoUserId, String newUserName);
/**
* 修改用户密文密码
*/
public void changeEncryPassword(String ssoUserId, String newEncryPassword);
/**
* 登录,需要用户名,密文密码和登录来源标识,每个web系统对应一个独立的登录来源标识。
* 登录时会更新最后登录时间和当前登录令牌值
*/
public SSOUser login(String userName, String encryPassword, String loginClientKey);
/**
* 根据cookie中的登录令牌获取用户
* 能获取到
*/
public SSOUser getLoginUser(String currentLoginToken);
/**
* 当发现登录超时时调用登出
*/
public String logout(String currentLoginToken);
/**
* 获取用于保存用户登录令牌的cookie字段名常量
*/
public String getLoginTokenCookieName();
/**
* 获取用于保存用户最后请求时间戳的cookie字段名常量
*/
public String getLastOperateTimeCookieName();
...
}
jar包中提供一套该接口的默认实现,基于mysql存储基本用户,基于redis存储用户登录信息。
使用该默认实现时,各子系统应采用同一mysql和redis数据源,提供方式为在WEB-INF\classes
目录放置一个sso_datasource.properties
,内容如下:
mysql_url=
mysql_username=
mysql_password=
redis_ip=
redis_port=
各系统也可以在约定好的情况下集体换用新的实现。
无论是从各子系统独立的注册和登录页面,还是从公用的注册和登录页面登录,在用户注册或登录时,都需要调一下 BaseUserService
接口进行公共用户信息的注册和登录,如果要更换该接口的实现,应该所有子系统一起更换。
在任一子系统登录后,BaseUser
表中更新保存了本次登录生成的登录令牌,默认实现中令牌的值是一个根据帐号信息、登录来源、时间戳生成的32位MD5码,该令牌信息应由子系统写入到cookie中供所有子系统读取,cookie名称通过getLoginTokenCookieName()
方法获取常量。
在每次用户发起http请求时,子系统应更新cookie中最后操作时间戳,cookie名称通过getLastOperateTimeCookieName()
方法获取常量。
验证登录状态时,子系统首先从cookie中取出登录令牌,调用getLoginUser(String currentLoginToken)
方法验证登录状态。此时默认实现会从redis中根据用户的最后一次登录令牌为key
,查询登录时保存于map
中的用户。然后子系统通过对cookie中的最后操作时间来自行判断是否登录超时,若超时调用logout(String currentLoginToken)
。
当各子系统不在同一域名下时,采用本方案。本方案为基于api开放接口及oauth2授权认证的中央通行证方案。
本方案提供一个独立的web应用(下称通行证系统),包括中央注册登录页面、用户授权管理中心,以及运营管理后台。
英文变量名 | 中文名 | 备注 |
---|---|---|
accessToken | 临时令牌 | 一个临时的字符串标识符,有效期较短,用于追踪一个用户跳转到通行证系统进行注册登录流程,然后再通过服务端接口获取流程结果的一个流程追踪标识。每当要开启新注册和登录流程时,应用系统可以向通行证系统申请一个 |
refreshToken | 正式授权令牌 | 在完成了注册或登录流程后,用accessToken通过服务端接口换取到的正式授权令牌,有很长的有效期,在有效期内可以凭该令牌调用各种通行证系统提供的与用户有关的操作接口(例如查询用户信息) |
loginToken | 登录令牌 | 通行证系统在自己的域名下的cookie中写入的,用于追踪用户登录状态的标识,每次用户跳转过来进行登录状态验证时,都会维持该令牌在cookie中继续存在,该令牌与跨域的应用子系统无关,是通行证系统及其同域子系统自用的 |
- accessToken每开启一个跨域注册或登录流程时会建一个
- refreshToken每当通行证系统确认要授予某个应用子系统对某个用户的操作权限时建一个
- loginToken每当用户成功登录时在通行证系统的cookie中写入一个
高可信子系统是指属于同一公司或团队开发,业务代码安全度完全可信,不会恶意窜改或伪造用户信息,可以持有用户敏感信息,因此该注册流程较为简化,省去了对accessToken的使用,允许用户在本地子系统提供的注册页面中操作并通过服务端接口进行用户信息同步。如果该子系统不打算做自己的注册页完全依赖通行证系统,则流程需要按**3.1.4 **低可信子系统注册流程来走。
- 在以下所有流程图中,实线箭头代表有页面跳转,虚线箭头代表服务端接口调用。
- 当涉及到url页面跳转时,url参数中都会带有md5验签机制,参数为明文,参与验签的有不在url中的密钥值。凡是通行证系统提供的url均为https访问。
- 当涉及到服务端接口请求时,除了验签机制,还有可能增加https证书或者来源ip白名单或者专线等安全机制。
- 在以下所有流程图中,假设应用子系统域名为www.a.com,通行证系统域名为www.sso.com
主要规则:
- 通行证系统用户信息中对提交了用户登录名、手机号、电子邮箱、身份证号四种身份信息的用户做分级处理,只提交用户登录名的为认证级别最低的1级用户,提交了身份证号的,为认证级别最高的4级用户。
在一个新注册信息提交到通行证系统时,根据他所提交的最高级别信息判断重复,假设他提交的最高级别信息是电子邮箱,则根据查询系统中是否有该电子邮箱注册用户来判断重复,如果已有,不允许注册,由应用子系统提示用户该邮箱号已在某某系统中注册过,可以通过该帐号直接登录。
应用子系统应保证只提交经过了校验的手机、邮箱、身份证号信息到通行证系统,未经校验的则只保留于本系统中不上报。对高可信子系统来说可以提这样的要求。如果应用子系统没有校验某种信息真实性的能力,比如身份证号,则不可以上报该类信息,只能留在本地系统中做参考。
通行证系统会提供手机验证码、邮箱验证链接、甚至身份证号校验等接口来辅助应用子系统核实用户真实信息。
高可信子系统由于可以进行本地登录,与通行证系统的交互中省去了对accessToken的使用。假如高可信子系统不打算做本地登录,则需要按**3.1.5 **低可信子系统的登录流程来走。
主要规则:
- 通行证系统提供了一个带回调地址参数的验证用户登录状态的url地址,在应用子系统觉得有必要校验用户登录状态时(包括www.a.com的cookie中无登录令牌和认为需要重新核实令牌有效性两种情形),就调用该url跳转到www.sso.com检查登录状态,再等待用户被重定向到回调地址来接收通行证系统的判断结果。
通行证系统是根据www.sso.com的cookie中的登录令牌来判断登录状态的,无论是否有令牌都通过回调地址告知应用子系统结果。为了进一步确保注册来源子系统合法性,登录令牌不通过重定向url明文带回,在判断为已登录时,只能在重定向中告知登录用户id,应用子系统需要发一个服务端请求拿用户id换取登录令牌确保令牌不被截走。如果判断为未登录,应用子系统将用户引导到登录页填写登录信息。
登录令牌代表了通行证授予应用子系统的权限凭证,权限内容由用户在登录页面的授权选择决定,后续章节详细说明。应用子系统拿到登录令牌后,不应直接写入www.a.com域名的cookie中随普通http传输,以免被偷走,可以另行写入一个cookie值维持登录状态,在cookie过期或要进行重要操作前,才会再次跳转到通行证系统的验证url进行登录状态验证。令牌只能用于对服务端进行用户相关接口调用时做为授权凭证。
由于用户在应用子系统中的操作行为通行证系统均不知情,应确保www.a.com的cookie中登录令牌失效较快(建议不超过15分钟),www.sso.com的cookie中登录令牌失效较慢(建议不少于1小时),提高应用子系统的重新验证机率来达到延续用户登录状态的目的,每次应用子系统使用验证url判断登录状态,就能在通行证系统中延长1小时的登录状态。但仍有可能出现应用子系统中一直在操作,而通行证系统中已过期,这时需要应用子系统重新进行登录。
登录帐号可以是用户登录名、手机号、电子邮箱、身份证号其一,前提是通过注册或后续资料补充上报过通行证系统。高可信子系统如果不想做本地的注册登录页,也可以直接将用户引导到通行证系统的注册登录页操作。
高可信子系统虽然本地数据库也有用户注册信息可自行做登录判断,但不通过通行证系统来判断则无法为在其他子系统注册的用户进行登录。
低可信子系统一般是指外部合作伙伴开发的web系统,代码不受控制,要进行严格的安全防范。此时的实现方案基于oauth2授权方式,类似于QQ、支付宝帐号登第三方帐号登录形式。
主要规则:
- 在该流程中accessToken是用于注册过程追踪的临时令牌,会在url中传输,即使被第三方截取偷走,也无法有效对url请求进行验签,即使能成功通过验签完成了用户注册,也没机会换取到正式令牌refreshToken,因为服务端请求无法伪造。