web前后端分离系统之间的单点登录主要难点在于系统之间如何跳转以及跨域传递cookie。首先给出单点登录示意图。
1.百度系统未登录,请求登录,其中参数callBackUrl是回调地址
$.ajax({
url : "http://localhost:8181/infoUser/index.do?callBackUrl=http://localhost:8083/sys1_web/index.html",
type : "post",
contentType:"application/json;charset=utf-8",
async: false,
beforeSend: function(request) {
request.setRequestHeader("token", token);
},
success: function(result){
if(token){
addCookie("user",result.data);
location.reload();
}else{
var obj=JSON.parse(result)
console.log(result);
location.href= obj.data;
}
},onLoadError:function(){
}
});
2.百度系统后端进行拦截,判断是否具有全局票据,如果没有则跳转去登录,有则去验证全局票据是否有效,有效则返回临时票据。
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //验证全局门票 String ticket=request.getHeader("token"); String callBackUrl=request.getParameter("callBackUrl"); if(StrUtils.isStrEmpty(ticket)){ JSONResult result= JSONResult.ok("http://localhost:8083/sys1_web/login.html?callBackUrl="+callBackUrl); OutputStream out= response.getOutputStream(); out.write(JSON.toJSONString(result).getBytes("utf-8")); out.flush(); return false; } return true; }
3.Cas单点服务跳出登录页面,登录成功后,创建用户会话、全局票据、临时票据。
这里为了方便跳转以及后期cookie的设置,用到了 nginx反向代理
location /sys1_web/login.html {
proxy_pass http://localhost:8082/sso_web/login.html;
}
登录成功后,cas服务端产生用户会话、全局票据、临时票据。
InfoUserT infoUserT=infoUserTService.isExistUser(loginRo.getUserName()); if(infoUserT==null){ return JSONResult.errorMsg("用户不存在"); } if(!infoUserT.getUserPwd().equals(loginRo.getUserPwd())){ return JSONResult.errorMsg("密码错误"); } redisUtils.set(Const.USER_TOKON+infoUserT.getUserId(), JSON.toJSONString(infoUserT)); //全局门票 String userTicket= UUID.randomUUID().toString().replaceAll("-", "")+ DateUtil.dateToStr(new Date(),"yyyyMMddHHmmss"); redisUtils.setex(Const.USER_SESSION_TICKET+userTicket,infoUserT.getUserId(),60*60*24); //临时门票 String userTempTicket= UUID.randomUUID().toString().replaceAll("-", "")+ DateUtil.dateToStr(new Date(),"yyyyMMddHHmmss"); redisUtils.setex(Const.USER_TEMP_TICKET+userTempTicket, Sha256Util.getSHA256(userTempTicket),60*60); Map ticketMap=new HashMap(); ticketMap.put("userTicket",userTicket); ticketMap.put("userTempTicket",userTempTicket); return JSONResult.ok(ticketMap);
4.百度系统得到临时票据后向cas服务端进行验证临时票据是否有效。
@ApiOperation(value = "验证临时票据") @RequestMapping(path="/verificationTempTicket") public JSONResult verificationTempTicket(@RequestParam Map<String, Object> params, HttpServletRequest request,HttpServletResponse response){ String tempTicket=request.getHeader("token"); if(StrUtils.isStrEmpty(tempTicket)){ tempTicket= (String) params.get("token"); } String userTicket= (String) params.get("ticket"); String jmTicket=Sha256Util.getSHA256(tempTicket); if(!StrUtils.isStrEmpty((String) redisUtils.get(Const.USER_TEMP_TICKET+tempTicket)) && jmTicket.equals((String) redisUtils.get(Const.USER_TEMP_TICKET+tempTicket))){ String userJson=""; if(!StrUtils.isStrEmpty(userTicket)){ String userId= (String) redisUtils.get(Const.USER_SESSION_TICKET+userTicket); if(!StrUtils.isStrEmpty(userId)){ userJson = (String) redisUtils.get(Const.USER_TOKON+userId); } } redisUtils.del(Const.USER_TEMP_TICKET+tempTicket);//验证后删除临时票据 return JSONResult.ok(userJson); }else{ return JSONResult.errorMsg("临时票据验证不通过"); } }
5.如果临时票据有效则跳转到百度主页面
$.ajax({
url : "http://localhost:8081/InfoUserT/verificationTempTicket",
type : "post",
async: false,
beforeSend: function(request) {
request.setRequestHeader("token", data.userTempTicket);
},
data:{"ticket":data.userTicket},//json对象转换成字符串
success: function(rs){
console.log(rs);
if(rs.status==200){
console.log(rs);
addCookie("user", rs.data);
location.href=callBackUrl;
}else{
}
},onLoadError:function(){
}
});
6. 点击去新浪逛逛,通过传递全局票据,如果全局票据有效,则返回临时票据,临时票据验证成功后,返回到新浪主页面。
String ticket=request.getHeader("token"); String url="http://localhost:8081/InfoUserT/verificationTicket?token="+ticket;//验证全局票据是否有效 String result=httpGet(url); JSONObject jsonObject=JSONObject.parseObject(result); if(jsonObject.getIntValue("status")==200){ String data=jsonObject.getString("data"); JSONObject jsonData=JSONObject.parseObject(data); String temp_ticket=jsonData.getString("userTempTicket"); url="http://localhost:8081/InfoUserT/verificationTempTicket?token="+temp_ticket+"&ticket="+ticket;//临时票据是否有效 result=httpGet(url); jsonObject=JSONObject.parseObject(result); if(jsonObject.getIntValue("status")==200){ return JSONResult.ok(jsonObject.getString("data")); } }else{ return JSONResult.ok("http://localhost:8182/sys2_web/login.html"); } return JSONResult.ok();