一.实现原理
SSO是一种统一认证和授权机制,指访问同一服务器不同应用中的受保护资源的同一用户,只需要登录一次,即通过一个应用中的安全验证后,再访问其他应用中的受保护资源时,不再需要重新登录验证。简单的说就是在一个多系统共存的环境下,用户在某一系统中登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。
二.实现方式
方式1:共享session
1.web.xml
<!-- Spring Session Filter --> <filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<filter> <filter-name>sso</filter-name> <filter-class>com.capital.bbs.filter.UserSsoLoginVerifyFilter</filter-class> </filter> <filter-mapping> <filter-name>sso</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>sso</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <filter-mapping> <filter-name>sso</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping>
2.添加jar包: jedis-x.x.x.jar
3.redis.properties
jedis.host=127.0.0.1 jedis.port=7408 jedis.password=123123 jedis.timeout=30000 jedis.pool.maxTotal=200 jedis.pool.minIdle=1 jedis.pool.maxIdle=50 jedis.pool.maxWaitMillis=5000 jedis.pool.testOnBorrow=true jedis.pool.testOnReturn=true
4.applicationContext_client.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:http-conf="http://cxf.apache.org/transports/http/configuration" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd "> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <import resource="classpath:META-INF/cxf/cxf-extension-xml.xml"/> <import resource="classpath:META-INF/cxf/cxf-extension-http.xml"/> <http-conf:conduit name="*.http-conduit"> <http-conf:client ConnectionTimeout="180000" ReceiveTimeout="300000"/> </http-conf:conduit> <!-- redis --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="${jedis.pool.maxTotal}"/> <property name="maxIdle" value="${jedis.pool.maxIdle}"/> <property name="minIdle" value="${jedis.pool.minIdle}"/> <property name="maxWaitMillis" value="${jedis.pool.maxWaitMillis}"/> <property name="testOnBorrow" value="${jedis.pool.testOnBorrow}"/> <property name="testOnReturn" value="${jedis.pool.testOnReturn}"/> </bean> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${jedis.host}" /> <property name="port" value="${jedis.port}" /> <property name="password" value="${jedis.password}" /> <property name="timeout" value="${jedis.timeout}" /> <property name="poolConfig" ref="jedisPoolConfig" /> <property name="usePool" value="true" /> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> </bean> <!-- 将session放入redis --> <bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> <property name="maxInactiveIntervalInSeconds" value="1800" /> </bean> <bean id="defaultCookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer"> <property name="cookieName" value="WEB_SESSION_ID" /> <property name="cookiePath" value="/" /> </bean> <bean id="cookieHttpSessionStrategy" class="org.springframework.session.web.http.CookieHttpSessionStrategy"> <property name="cookieSerializer" ref="defaultCookieSerializer" /> </bean> </beans>
5.applicationContext_hibernate.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- JDBC参数配置 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" lazy-init="true"> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> <property name="ignoreResourceNotFound" value="true" /> <property name="locations"> <list> <value>classpath:/jdbc.properties</value> <!--begin redis sso--> <value>classpath:/redis.properties</value> <!--end redis sso--> </list> </property> </bean> </beans>
6.UserSsoLoginVerifyFilter过滤器示例:
import com.capital.bbs.common.criteria.UserCriteria; import com.capital.bbs.common.dto.user.UserDTO; import com.capital.bbs.manager.user.IUserManager; import com.capital.common.vo.user.UserVO; import com.opensymphony.xwork2.ActionContext; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class UserSsoLoginVerifyFilter extends StrutsPrepareAndExecuteFilter { private static final Logger LOGGER = LogManager.getLogger(UserSsoLoginVerifyFilter.class); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; //当前系统登录存放的session Object userOid = request.getSession().getAttribute("userOid"); if (null == userOid) { //单点登录,其他相关系统共享存放的session UserVO user = (UserVO) ActionContext.getContext().getSession().get("loginUser"); if (null != user) { try { ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext()); IUserManager userManager = (IUserManager) ac.getBean("userManager"); UserCriteria userCriteria = new UserCriteria(); UserDTO userDTO = new UserDTO(); //通过共享session里面的用户名获取用户信息 userDTO.setUserName(user.getUserName()); userDTO.setUserOid(user.getUserOid()); userCriteria.setUser(userDTO); userDTO = userManager.doSsoLogin(userCriteria); if (null != userDTO) { request.getSession().setAttribute("userOid", user.getUserOid()); } }catch (Exception e){ LOGGER.error("单点登录过滤器中查询用户信息失败"); } } } filterChain.doFilter(request, response); } }
三.约束:
1.session中所涉及的类型必须是子系统中共同拥有的(即程序集、类型都需要一致),这导致session的使用受到诸多限制;
2.跨顶级域名的情况完全无法处理。