- 什么是SSO
SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。
- 传统的登录流程
-
-
- 传统流程中的问题:
、
- 在集群环境中每个工程都有自己的session,如果把用户信息写入session而不共享的话,会出现用户反复登录的情况。
-
第一种方案: 可以配置tomcat的session共享。配置tomcat集群。Tomcat配置好集群后,会不停的向集群中其他的tomcat广播自己的session信息。其他的tomcat做session同步。可以保证所有的tomcatsession中的内容都是一致的。优点:不用修改代码就可以实现session共享。
- 第二种方案 缺点:需要程序员开发
实现单点登录系统,提供服务接口。把session数据存放在redis。Redis可以设置key的生存时间、访问速度快效率高。
优点:redis存取速度快,不会出现多个节点session复制的问题。效率高。
-
缺点:tomcat 是全局session复制,集群内每个tomcat的session完全同步(也就是任何时候都完全一样的) 在大规模应用的时候,用户过多,集群内tomcat数量过多,session - 单点登录系统流程
- 创建单点登录系统
-
单点登录系统是一个独立的工程。需要操作redis、连接数据库。
-
-
实现方案:登录接口举例:sso单点登录单独为一个项目,提供检查登录信息是否可用,注册接口,登录接口,下面举例为登录接口:
-
@Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping("/check/{param}/{type}") @ResponseBody public Object checkData(@PathVariable String param, @PathVariable Integer type, String callback) { TaotaoResult result = null; //参数有效性校验 if (StringUtils.isBlank(param)) { result = TaotaoResult.build(400, "校验内容不能为空"); } if (type == null) { result = TaotaoResult.build(400, "校验内容类型不能为空"); } if (type != 1 && type != 2 && type != 3 ) { result = TaotaoResult.build(400, "校验内容类型错误"); } //校验出错 if (null != result) { if (null != callback) { MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result); mappingJacksonValue.setJsonpFunction(callback); return mappingJacksonValue; } else { return result; } } //调用服务 try { result = userService.checkData(param, type); } catch (Exception e) { result = TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } if (null != callback) { MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result); mappingJacksonValue.setJsonpFunction(callback); return mappingJacksonValue; } else { return result; } } //创建用户 @RequestMapping(value="/register", method=RequestMethod.POST) @ResponseBody public TaotaoResult createUser(TbUser user) { try { TaotaoResult result = userService.createUser(user); return result; } catch (Exception e) { e.printStackTrace(); return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } } //用户登录 @RequestMapping(value="/login", method=RequestMethod.POST) @ResponseBody public TaotaoResult userLogin(String username, String password, HttpServletRequest request, HttpServletResponse response) { try { TaotaoResult result = userService.userLogin(username, password, request, response); return result; } catch (Exception e) { e.printStackTrace(); return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } } @RequestMapping("/token/{token}") @ResponseBody public Object getUserByToken(@PathVariable String token, String callback) { TaotaoResult result = null; try { result = userService.getUserByToken(token); } catch (Exception e) { e.printStackTrace(); result = TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } //判断是否为jsonp调用 if (StringUtils.isBlank(callback)) { return result; } else { MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result); mappingJacksonValue.setJsonpFunction(callback); return mappingJacksonValue; } } }
-
/** * 用户管理Service * <p>Title: UserServiceImpl</p> * <p>Description: </p> * <p>Company: www.itcast.com</p> * @author 入云龙 * @date 2015年9月14日上午10:28:39 * @version 1.0 */ @Service public class UserServiceImpl implements UserService { @Autowired private TbUserMapper userMapper; @Autowired private JedisClient jedisClient; @Value("${REDIS_USER_SESSION_KEY}") private String REDIS_USER_SESSION_KEY; @Value("${SSO_SESSION_EXPIRE}") private Integer SSO_SESSION_EXPIRE; @Override public TaotaoResult checkData(String content, Integer type) { //创建查询条件 TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); //对数据进行校验:1、2、3分别代表username、phone、email //用户名校验 if (1 == type) { criteria.andUsernameEqualTo(content); //电话校验 } else if ( 2 == type) { criteria.andPhoneEqualTo(content); //email校验 } else { criteria.andEmailEqualTo(content); } //执行查询 List<TbUser> list = userMapper.selectByExample(example); if (list == null || list.size() == 0) { return TaotaoResult.ok(true); } return TaotaoResult.ok(false); } @Override public TaotaoResult createUser(TbUser user) { user.setUpdated(new Date()); user.setCreated(new Date()); //md5加密 user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes())); userMapper.insert(user); return TaotaoResult.ok(); } /** * 用户登录 * <p>Title: userLogin</p> * <p>Description: </p> * @param username * @param password * @return * @see com.taotao.sso.service.UserService#userLogin(java.lang.String, java.lang.String) */ @Override public TaotaoResult userLogin(String username, String password, HttpServletRequest request, HttpServletResponse response) { TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(username); List<TbUser> list = userMapper.selectByExample(example); //如果没有此用户名 if (null == list || list.size() == 0) { return TaotaoResult.build(400, "用户名或密码错误"); } TbUser user = list.get(0); //比对密码 if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) { return TaotaoResult.build(400, "用户名或密码错误"); } //生成token String token = UUID.randomUUID().toString(); //保存用户之前,把用户对象中的密码清空。 user.setPassword(null); //把用户信息写入redis jedisClient.set(REDIS_USER_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user)); //设置session的过期时间 jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE); //添加写cookie的逻辑,cookie的有效期是关闭浏览器就失效。 CookieUtils.setCookie(request, response, "TT_TOKEN", token); //返回token return TaotaoResult.ok(token); } @Override public TaotaoResult getUserByToken(String token) { //根据token从redis中查询用户信息 String json = jedisClient.get(REDIS_USER_SESSION_KEY + ":" + token); //判断是否为空 if (StringUtils.isBlank(json)) { return TaotaoResult.build(400, "此session已经过期,请重新登录"); } //更新过期时间 jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE); //返回用户信息 return TaotaoResult.ok(JsonUtils.jsonToPojo(json, TbUser.class)); } }