SSO英文全称Single Sign On,单点登陆。SSO是在多个应用系统中,用户只须要登陆一次就能够访问全部相互信任的应用系统。它包括能够将此次主要的登陆映射到其余应用中用于同一个用户的登陆的机制。它是目前比较流行的企业业务整合的解决方案之一。javascript
为何须要单点登陆?html
咱们通常经过session设计登陆,这种对于只有一个工程来讲是没有问题的。可是对于分布式系统来讲,因为牵涉到多个子系统,若是每个系统都要输入一遍用户名,密码;这会很是麻烦。所以,单点登陆应运而生。java
实现原理git
将登陆系统单独摘出来,作成一个登陆子系统。请求登录时候访问这个子系统,当登录验证经过的时候,生成一个token存入网站顶级域名下的cookie当中。将与这个token对应的用户状态信息存入缓存中去,并设置生存时间,此处缓存使用的是redis。当其余的子系访问必须登陆才可访问的资源时候,必须先到登陆系统进行验证token的存在性以及是否过时,验证经过才可进行访问。ajax
token:这至关于一个令牌,无论你在哪一个子系统下面,想要获取登录状态就须要得到单点登陆系统生成的token,这个token是在登录成功时候生成,生成后保存到redis(缓存)中做为key,key生成规则使用的是UUID,目的是为了防止多个用户登陆时候产生冲突,value为用户的信息,并设置有效时间。同时将这个key保存到cookie当中。这样当你登陆其余子系统时须要验证登陆状态的时候,能够经过获取浏览器cookie从而取出这个token并去缓存中查询是否存在相应数据,若是存在,就说明是在登录状态。若是token存在而缓存中不存在说明用户登陆状态过时了。若是没有token,说明用户根本没有登陆。redis
具体实现(util以及Result是我本身封装的类,根据我的编码习惯而已)数据库
/** * 单点登陆,登陆成功后会生成一个token保存在cookie当中 * @param user 登陆用户信息 * @param request 获取登陆相关数据 * @param response 回复 * @return 返回登陆结果 */ @RequestMapping(value = "/login",method = RequestMethod.POST) @ResponseBody public Result login(User user,HttpServletRequest request, HttpServletResponse response){ String url=request.getParameter("url"); //从service层获取信息,若是登录成功里面会保存有token Result result= this.userService.login(user); //将cookie写入本地根域名下面 if (result.getStatus().equals(200)){ CookiteUtils.setCookie(request,response,TOKEN_ID, (String) result.getData()); //若是url不为空,直接跳转回原来访问的页面 if (!StringUtils.isBlank(url)){ try { response.sendRedirect(url); } catch (IOException e) { e.printStackTrace(); } } } return result; }
/** * * @param user 用户登陆相关信息 * @des: Result是我本身封装的返回类,登陆时将token和用户信息存入redis * @return */ public Result login(User user){ //先检验用户名与密码是否为空 if (StringUtils.isBlank(user.getUsername())|| StringUtils.isBlank(user.getPassword())){ return Result.build(400,"用户名或密码不能为空"); } //转为UUID与数据库中的用户名密码进行比较 user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes())); //根据用户的用户名密码查询信息 User temp=userMapper.findByUsernameAndPassword(user); //若是用户名密码正确会查询出信息,不对的话没法查询到 if(temp==null){ return Result.build(400,"用户名或密码不正确"); } //查询到的话生成token做为键,用户的信息做为值保存到redis中 String token_ID=UUID.randomUUID().toString().replaceAll("-",""); temp.setPassword(null); jedis.set(TOKEN_ID+":"+token_ID, JsonUtils.objectToJson(temp)); jedis.expire(TOKEN_ID+":"+token_ID,300); //返回给controller层,并从controller层中将token保存到根cookie中 return Result.ok(token_ID); }
最后经过jsonp进行跨域请求来进行测试,是否可以实现单点登陆的功能。json
//跨域请求,使用jsonp var _ticket=$.cookie("TOKEN_ID"); if (!_ticket){ return; } $.ajax({ type:"GET", dataType:"jsonp", url:"http://localhost:8082/user/token/"+_ticket,//这个就是验证是否登陆或者登陆是否超时,由于咱们咱们放入redis缓存的时候设置了有效时长 success: function (data) { //登陆成功显示用户名 if (data.status==200){ var username=data.data.username; var html=username; $("#checkLogin").html(html); } } });
demo地址:https://gitee.com/CloseHeart/SOS跨域