单点登陆(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只须要登陆一次就能够访问全部相互信任的应用系统。css
实现单点登陆,就是解决如何产生和存储那个信任,再就是其余系统如何验证这个信任的有效性。所以,也就须要解决如下两点:html
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 添加服务消费者的标志 --> <dubbo:application name="ego-sso-web-consumer"/> <!-- 指定注册中心,有两个地址192.168.1.17一、172.18.25.171 --> <dubbo:registry address="172.18.25.171:2181,172.18.25.171:2182,172.18.25.171:2183" protocol="zookeeper" /> <!--<dubbo:registry address="192.168.1.171:2181,192.168.1.171:2182,192.168.1.171:2183" protocol="zookeeper" />--> <!-- spring容器中存在一个远程服务的代理对象 --> <dubbo:reference interface="com.soldier.ego.rpc.service.UserService" id="userServiceProxy"></dubbo:reference> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 加载cache.properties--> <!--<context:property-placeholder location="classpath:cache.properties"></context:property-placeholder>--> <!-- 实例化JedisCluster,链接redis集群,有两个地址192.168.1.17四、172.18.25.174--> <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"> <constructor-arg name="nodes"> <set> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="172.18.25.174"></constructor-arg> <constructor-arg name="port" value="6380"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="172.18.25.174"></constructor-arg> <constructor-arg name="port" value="6381"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="172.18.25.174"></constructor-arg> <constructor-arg name="port" value="6382"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="172.18.25.174"></constructor-arg> <constructor-arg name="port" value="6383"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="172.18.25.174"></constructor-arg> <constructor-arg name="port" value="6384"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="172.18.25.174"></constructor-arg> <constructor-arg name="port" value="6385"></constructor-arg> </bean> </set> </constructor-arg> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.soldier.ego.sso.service.impl" /> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 扫描controller --> <context:component-scan base-package="com.soldier.ego.sso.controller" /> <!-- mvc:annotation-driven --> <mvc:annotation-driven /> <!-- 静态资源映射 --> <!-- location:表示资源在项目的真正位置 --> <!-- mapping:访问路径 --> <!-- /css/** --> <!-- http://localhost:8080/css/a/b/c/hello.css --> <!-- / = http://localhost:8080/ --> <mvc:resources location="/css/" mapping="/css/**"></mvc:resources> <mvc:resources location="/js/" mapping="/js/**"></mvc:resources> <mvc:resources location="/images/" mapping="/images/**"></mvc:resources> <!-- 视图解析器 --> <bean id="viewResovler" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 表示使用的视图技术是jsp --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> <!-- 前缀 --> <property name="prefix" value="/WEB-INF/jsp/"></property> <!-- 后缀 --> <property name="suffix" value=".jsp"></property> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>ego-sso-web</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <!--<servlet-mapping>--> <!--<servlet-name>default</servlet-name>--> <!--<url-pattern>/favicon.ico</url-pattern>--> <!--</servlet-mapping>--> <!-- 以监听器的方式启动spring容器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 指定spring的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext-*.xml</param-value> </context-param> <!-- POST请求的乱码过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <!-- 指定编码方式 --> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <!-- 映射filter --> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- springmvc的servlet --> <servlet> <servlet-name>ego-sso-web</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定springmvc的配置文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> <!-- 让springmvc随系统启动而启动 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ego-sso-web</servlet-name> <!-- 不须要伪静态化--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
请求方法 | GET |
UR | http://sso.egou.com/user/check/{param}/{type} |
参数说 | 格式如:zhangsan/1,其中 zhangsan 是校验的数据,type 为类型,可 选参数 一、二、3 分别表明 username、phone、email 可选参数 callback:若是有此参数表示此方法为 jsonp 请求,须要支 持 jsonp。 |
示例 | http://sso.egou.com/user/check/zhangsan/1 |
返回 | { status: 200 //200 成功 msg: "OK"// 返回信息消息 data: false // 返回数据,true:数据可用,false:数据不可用 } |
/** * 处理用户名惟一性验证请求 * @param param 验证数据 * @param type 验证类型 * @param callback 回到函数 * MappingJacksonValue --> 返回json,支持JSONP(实际上是解决JS跨域调用数据的一种方案) * required = false --> 非必须 * @ResponseBody 异步的,不会进行跳转 */ @RequestMapping("/user/check/{param}/{type}") @ResponseBody public MappingJacksonValue loadPage(@PathVariable String param, @PathVariable Integer type, @RequestParam(required = false) String callback) { EgoResult result = ssoUserService.selectUserByCond(param, type); // 处理json响应数据格式 MappingJacksonValue jacksonValue = new MappingJacksonValue(result); if (!StringUtils.isEmpty(callback)) jacksonValue.setJsonpFunction(callback); return jacksonValue; }
@Service public class SsoUserServiceImpl implements SsoUserService { //注入的是远程服务的代理对象 @Autowired private UserService userServiceProxy; @Override public EgoResult selectUserByCond(String cond, Integer type) { return userServiceProxy.selectUserByCondService(cond, type); } }
@Service public class UserServiceImpl implements UserService { @Autowired private TbUserMapper tbUserMapper; @Override public EgoResult selectUserByCondService(String cond, Integer type) { //动态产生where条件 TbUserExample example = new TbUserExample(); TbUserExample.Criteria criteria = example.createCriteria(); //封装查询条件 if (type.equals(1)) { criteria.andUsernameEqualTo(cond); } else if (type.equals(2)) { criteria.andPhoneEqualTo(cond); } else if (type.equals(3)) { criteria.andEmailEqualTo(cond); } List<TbUser> userList = tbUserMapper.selectByExample(example); // 建立EgoResult对象 EgoResult result = new EgoResult(); result.setStatus(200); result.setMsg("ok"); if (userList!=null && userList.size()>0) result.setData(false); else result.setData(true); // 用户名可用 return result; } }
请求方法 | POST |
URL | http://sso.egou.com/user/register |
参数说明 | username //用户名 password //密码 phone //手机号 email //邮箱 |
示例 | http://sso.egou.com/user/register |
返回值 | { status: 400 msg: "注册失败. 请校验数据后请再提交数据." data: null } |
/** * 实现用户注册 * @param user 用户信息 * @ResponseBody 异步的,不会进行跳转 */ @RequestMapping(value = "/user/register", method = RequestMethod.POST) @ResponseBody public EgoResult insertUser(TbUser user) { return ssoUserService.insertUserService(user); }
@Service public class SsoUserServiceImpl implements SsoUserService { //注入的是远程服务的代理对象 @Autowired private UserService userServiceProxy; @Override public EgoResult insertUserService(TbUser user) { // md5加密 String pwd = user.getPassword(); String md5 = DigestUtils.md5DigestAsHex(pwd.getBytes()); user.setPassword(md5); return userServiceProxy.insertUserService(user); } }
@Service public class UserServiceImpl implements UserService { @Autowired private TbUserMapper tbUserMapper; @Override public EgoResult insertUserService(TbUser user) { EgoResult result = new EgoResult(); try { Date date = new Date(); user.setCreated(date); user.setUpdated(date); tbUserMapper.insert(user); result.setStatus(200); result.setMsg("注册成功."); } catch (Exception e) { result.setStatus(400); result.setMsg("注册失败. 请校验数据后请再提交数据."); e.printStackTrace(); } return result; } }
请求方式 | POST |
URL | htt://sso.eou.com/user/l |
参数说明 | username //用户名 password //密码 |
示例 | http://sso.egou.com/user/loginusername=zhangsan&password=123 |
返回值 | { status: 200 msg: "OK" data: "fe5cb546aeb3ce1bf37abcb08a40493e"//登陆成功,返 回 token } |
/** * 实现用户登陆 * @param username 用户名 * @param password 密码 * @ResponseBody 异步的,不会进行跳转 */ @RequestMapping(value = "/user/login", method = RequestMethod.POST) @ResponseBody public EgoResult userLogin(String username, String password) { return ssoUserService.userLogin(username, password); }
@Service public class UserServiceImpl implements UserService { @Autowired private TbUserMapper tbUserMapper; @Override public TbUser selectUserByUserNameService(String username) { //动态产生where条件 TbUserExample example = new TbUserExample(); TbUserExample.Criteria criteria = example.createCriteria(); //封装查询条件 criteria.andUsernameEqualTo(username); // where username=? List<TbUser> userList = tbUserMapper.selectByExample(example); // 由于用户名惟一 if (userList!=null && userList.size()==1) return userList.get(0); return null; } }
@Service public class SsoUserServiceImpl implements SsoUserService { //注入的是远程服务的代理对象 @Autowired private UserService userServiceProxy; // 注入JedisCluster集群访问对象 @Autowired private JedisCluster jedisCluster; @Override public EgoResult userLogin(String username, String password) { EgoResult result = new EgoResult(); result.setStatus(400); result.setData(null); result.setMsg("用户名或密码错误."); TbUser tbUser = userServiceProxy.selectUserByUserNameService(username); if (tbUser != null) { //对前端提交的密码进行加密 password = DigestUtils.md5DigestAsHex(password.getBytes()); if (password.equals(tbUser.getPassword())) { // 将当前登陆用户对象,转为json字符串,保存到redis数据库 String userJson = JsonUtils.objectToJson(tbUser); String token = UUID.randomUUID().toString(); // 将用户信息保存到redis数据库 jedisCluster.set(token, userJson); result.setStatus(200); result.setMsg("登陆成功."); result.setData(token); } } return result; } }