SSO中的Pattern

   SSO方案中太多平行对称的分支选择,就像博而赫斯那小径分岔的花园。刚手写完一个超迷你劲袖珍的SSO,顺着 SAML2.0和OpenID的规范,记录一下这些分岔点:

  1. 流程是从身份提供者还是消费者发起?
    身份提供者,也就是SSO Server了,又叫Id Provider,简称Idp。而身份消费者,SSO Client,在SAML里叫做Sp。
    身份提供者发起流程中,用户登录进SSO Server,SSO Server展现一个Portal/菜单,上有到各SSO Client的URL若干,每个URL上都已经加了身份信息的料。
    身份消费者发起流程中,Portal/菜单里是无料的普通URL(甚至用户直接就先跑去了Client的网站),SSO Client发现本地Session里没有用户的身份信息,只好redirect重返SSO Server,最后Server再以有料的URL跳转回去。
    显然,前一种流程少了两次Redirect,速度更快,但Portal/菜单中的每个URL都深深的耦合着SSO Sever,又或者,有时根本就没有这么一个中央Portal的存在。
  2. URL参数中传送身份信息还是仅仅是指针
    SSO Server 向 Client跳转时,可以大大方方的把身份信息放在URL参数里直接传输,也可以小心翼翼的只扔过来一个随机字符串(SAML中叫 Artifact),Client拿着这个随机字串再到后台偷偷摸摸用WebService/REST接口问SSO Server拿到完整的身份信息。
    显然,前一种方式少了一次后台查询,速度更快,但没有后一种方式安全,后一种方式的WebService/REST接口还可传输任意格式的很多很多的信息。
  3. 安全措施是全文加密还是仅仅签名
    有人喜欢全文(再加一个nonce )加密成长长一段密文,心中充满了安全感。也有人大胆的明文传输内容,只在最后加个签名来防伪(签名用简单的明文+密钥散列,或者HMAC加密散列算法)
    前者用户只看到长长一段火星文,不会让用户偷窥到什么,但字串暴长且加解密本身比较消耗CPU。
  4. SSO Client会自己解密/比对签名吗?
    一般,SSO Client会根据约定自己解密或根据明文生成签名进行比对,还要负责验证那个nonce不会已被使用或者已经超时等等。
    但也会可以有很懒的client,不懂这么复杂的安全加密算法,直接把收到的内容在后台用WebService/Rest接口发回给SSO Server帮忙搞定。



    刚刚手写那个SSO,身份提供者发起流程,在URL参数中传输加密的身份信息,由SSO Client自行解密,so,超简单...

  安民告示:
     由于晓钢的服务器出了点故障,SpringSide的主页和Wiki暂时不能访问,而以下部分是完好的:

  • 论坛:http://forum.springside.org.cn
  • 源码:http://springside.googlecode.com/svn/springside3/trunk/
  • 下载:http://www.sf.net/projects/springside

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Spring SSO代码案例: 1. 首先,需要在两个应用程序添加以下依赖项: ```xml <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>5.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>5.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-saml2-core</artifactId> <version>1.0.10.RELEASE</version> </dependency> ``` 2. 在两个应用程序的Spring配置文件,配置Spring Security和SAML: ```xml <!-- 应用程序1的配置文件 --> <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager"> <constructor-arg> <list> <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider"> <constructor-arg value="http://localhost:8081/metadata"/> <constructor-arg value="5000"/> <property name="parserPool" ref="parserPool"/> </bean> </list> </constructor-arg> </bean> <bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint"> <property name="defaultProfileOptions"> <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions"> <property name="includeScoping" value="false"/> </bean> </property> </bean> <bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter"> <constructor-arg> <bean class="org.springframework.security.saml.metadata.CachingMetadataManager"> <constructor-arg> <list> <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider"> <constructor-arg value="http://localhost:8080/metadata"/> <constructor-arg value="5000"/> <property name="parserPool" ref="parserPool"/> </bean> </list> </constructor-arg> </bean> </constructor-arg> </bean> <bean id="SAMLUserDetailsService" class="com.example.sso.SAMLUserDetailsServiceImpl"/> <bean id="samlWebSSOFilter" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"> <constructor-arg> <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions"> <property name="includeScoping" value="false"/> </bean> </constructor-arg> <property name="userDetailsService" ref="SAMLUserDetailsService"/> </bean> <bean id="samlLogoutFilter" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"> <property name="ssoLogoutURL" value="http://localhost:8081/logout"/> </bean> <bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"> <constructor-arg> <bean class="org.springframework.security.saml.websso.SingleLogoutProfileOptions"> <property name="requireSignedLogoutMessages" value="false"/> </bean> </constructor-arg> </bean> <security:http entry-point-ref="samlEntryPoint"> <security:intercept-url pattern="/saml/**" access="IS_AUTHENTICATED_FULLY"/> <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/> <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlWebSSOFilter"/> <security:custom-filter before="LOGOUT_FILTER" ref="samlLogoutFilter"/> <security:custom-filter after="LOGOUT_FILTER" ref="samlLogoutProcessingFilter"/> </security:http> <security:authentication-manager> <security:authentication-provider ref="SAMLAuthenticationProvider"/> </security:authentication-manager> <bean id="SAMLAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider"> <property name="userDetails" ref="SAMLUserDetailsService"/> <property name="forcePrincipalAsString" value="false"/> </bean> <!-- 应用程序2的配置文件 --> <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager"> <constructor-arg> <list> <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider"> <constructor-arg value="http://localhost:8080/metadata"/> <constructor-arg value="5000"/> <property name="parserPool" ref="parserPool"/> </bean> </list> </constructor-arg> </bean> <bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint"> <property name="defaultProfileOptions"> <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions"> <property name="includeScoping" value="false"/> </bean> </property> </bean> <bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter"> <constructor-arg> <bean class="org.springframework.security.saml.metadata.CachingMetadataManager"> <constructor-arg> <list> <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider"> <constructor-arg value="http://localhost:8081/metadata"/> <constructor-arg value="5000"/> <property name="parserPool" ref="parserPool"/> </bean> </list> </constructor-arg> </bean> </constructor-arg> </bean> <bean id="SAMLUserDetailsService" class="com.example.sso.SAMLUserDetailsServiceImpl"/> <bean id="samlWebSSOFilter" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"> <constructor-arg> <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions"> <property name="includeScoping" value="false"/> </bean> </constructor-arg> <property name="userDetailsService" ref="SAMLUserDetailsService"/> </bean> <bean id="samlLogoutFilter" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"> <property name="ssoLogoutURL" value="http://localhost:8080/logout"/> </bean> <bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"> <constructor-arg> <bean class="org.springframework.security.saml.websso.SingleLogoutProfileOptions"> <property name="requireSignedLogoutMessages" value="false"/> </bean> </constructor-arg> </bean> <security:http entry-point-ref="samlEntryPoint"> <security:intercept-url pattern="/saml/**" access="IS_AUTHENTICATED_FULLY"/> <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/> <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlWebSSOFilter"/> <security:custom-filter before="LOGOUT_FILTER" ref="samlLogoutFilter"/> <security:custom-filter after="LOGOUT_FILTER" ref="samlLogoutProcessingFilter"/> </security:http> <security:authentication-manager> <security:authentication-provider ref="SAMLAuthenticationProvider"/> </security:authentication-manager> <bean id="SAMLAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider"> <property name="userDetails" ref="SAMLUserDetailsService"/> <property name="forcePrincipalAsString" value="false"/> </bean> ``` 3. 创建一个SAMLUserDetailsService实现类: ```java @Service public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService { @Override public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException { String userId = credential.getNameID().getValue(); List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_USER")); return new User(userId, "", authorities); } } ``` 4. 启动两个应用程序,访问其一个应用程序的SAML登录页面,然后使用SAML凭据登录。您将被重定向到另一个应用程序,并自动登录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值