基于spring session 的方式只能解决session在同域或者是同服务的session共享问题。但是无法解决不同域名之前的共享问题,因此我们需要一台认证服务器。
sso思路
记住一个核心思想:建议一个公共的登陆点server,他登录了代表这个集团的产品就登录过了
有两个子系统app1、app2
登录app1
再去登录app2
登录客户端服务
第一层核心代码部分
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
... 其他判断
// 重点是这个
//valid login user, cookie + redirect
XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(req, res);
... xxlUser 没有xxlUser重新登录 有就可以登录访问系统
}
第二层
public static XxlSsoUser loginCheck(HttpServletRequest request, HttpServletResponse response){
String cookieSessionId = CookieUtil.getValue(request, Conf.SSO_SESSIONID);
// cookie user
XxlSsoUser xxlUser = SsoTokenLoginHelper.loginCheck(cookieSessionId);
if (xxlUser != null) {
return xxlUser;
}
// redirect user
// remove old cookie
SsoWebLoginHelper.removeSessionIdByCookie(request, response);
// set new cookie
String paramSessionId = request.getParameter(Conf.SSO_SESSIONID);
xxlUser = SsoTokenLoginHelper.loginCheck(paramSessionId);
if (xxlUser != null) {
CookieUtil.set(response, Conf.SSO_SESSIONID, paramSessionId, false); // expire when browser close (client cookie)
return xxlUser;
}
return null;
}
第三层 代码部分
/**
* login check
*
* @param sessionId
* @return
*/
public static XxlSsoUser loginCheck(String sessionId){
String storeKey = SsoSessionIdHelper.parseStoreKey(sessionId);
if (storeKey == null) {
return null;
}
XxlSsoUser xxlUser = SsoLoginStore.get(storeKey);
if (xxlUser != null) {
String version = SsoSessionIdHelper.parseVersion(sessionId);
if (xxlUser.getVersion().equals(version)) {
// After the expiration time has passed half, Auto refresh
if ((System.currentTimeMillis() - xxlUser.getExpireFreshTime()) > xxlUser.getExpireMinute()/2) {
xxlUser.setExpireFreshTime(System.currentTimeMillis());
SsoLoginStore.put(storeKey, xxlUser);
}
return xxlUser;
}
}
return null;
}
4.直接登录当前系统,并保存用户信息
第一层代码
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
... 其他判断
//valid login user, cookie + redirect
...
... xxlUser 没有xxlUser重新登录 有就可以登录访问系统
// ser sso user
request.setAttribute(Conf.SSO_USER, xxlUser);
// already login, allow
chain.doFilter(request, response);
return;
}
public static XxlSsoUser loginCheck(HttpServletRequest request, HttpServletResponse response){
String cookieSessionId = CookieUtil.getValue(request, Conf.SSO_SESSIONID);
// cookie user
XxlSsoUser xxlUser = SsoTokenLoginHelper.loginCheck(cookieSessionId);
...
SsoWebLoginHelper.removeSessionIdByCookie(request, response);
// set new cookie
String paramSessionId = request.getParameter(Conf.SSO_SESSIONID);
xxlUser = SsoTokenLoginHelper.loginCheck(paramSessionId);
...
return null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
... 其他判断
//valid login user, cookie + redirect
...
... xxlUser 没有xxlUser重新登录 有就可以登录访问系统
// valid login fail
if (xxlUser == null) {
String header = req.getHeader("content-type");
boolean isJson= header!=null && header.contains("json");
if (isJson) {
// json msg
res.setContentType("application/json;charset=utf-8");
res.getWriter().println("{\"code\":"+Conf.SSO_LOGIN_FAIL_RESULT.getCode()+", \"msg\":\""+ Conf.SSO_LOGIN_FAIL_RESULT.getMsg() +"\"}");
return;
} else {
// total link
String link = req.getRequestURL().toString();
// redirect logout
String loginPageUrl = ssoServer.concat(Conf.SSO_LOGIN)
+ "?" + Conf.REDIRECT_URL + "=" + link;
res.sendRedirect(loginPageUrl);
return;
}
}
}
这里我们排除用户名密码输入错误的情况
服务端就是登录保存cookie逻辑
@RequestMapping("/doLogin")
public String doLogin(HttpServletRequest request,
HttpServletResponse response,
RedirectAttributes redirectAttributes,
String username,
String password,
String ifRemember) {
boolean ifRem = (ifRemember!=null&&"on".equals(ifRemember))?true:false;
// valid login
ReturnT<UserInfo> result = userService.findUser(username, password);
if (result.getCode() != ReturnT.SUCCESS_CODE) {
redirectAttributes.addAttribute("errorMsg", result.getMsg());
redirectAttributes.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));
return "redirect:/login";
}
// 1、make xxl-sso user
XxlSsoUser xxlUser = new XxlSsoUser();
xxlUser.setUserid(String.valueOf(result.getData().getUserid()));
xxlUser.setUsername(result.getData().getUsername());
xxlUser.setVersion(UUID.randomUUID().toString().replaceAll("-", ""));
xxlUser.setExpireMinute(SsoLoginStore.getRedisExpireMinute());
xxlUser.setExpireFreshTime(System.currentTimeMillis());
// 2、make session id
String sessionId = SsoSessionIdHelper.makeSessionId(xxlUser);
// 3、login, store storeKey + cookie sessionId
SsoWebLoginHelper.login(response, sessionId, xxlUser, ifRem);
// 4、return, redirect sessionId
String redirectUrl = request.getParameter(Conf.REDIRECT_URL);
if (redirectUrl!=null && redirectUrl.trim().length()>0) {
String redirectUrlFinal = redirectUrl + "?" + Conf.SSO_SESSIONID + "=" + sessionId;
return "redirect:" + redirectUrlFinal;
} else {
return "redirect:/";
}
}
3.对原来的sessionId处理由用户id和用户版本号构成
/**
* make client sessionId
*
* @param xxlSsoUser
* @return
*/
public static String makeSessionId(XxlSsoUser xxlSsoUser){
String sessionId = xxlSsoUser.getUserid().concat("_").concat(xxlSsoUser.getVersion());
return sessionId;
}
public static void login(HttpServletResponse response,
String sessionId,
XxlSsoUser xxlUser,
boolean ifRemember) {
String storeKey = SsoSessionIdHelper.parseStoreKey(sessionId);
if (storeKey == null) {
throw new RuntimeException("parseStoreKey Fail, sessionId:" + sessionId);
}
SsoLoginStore.put(storeKey, xxlUser);
CookieUtil.set(response, Conf.SSO_SESSIONID, sessionId, ifRemember);
}
/**
* parse storeKey from sessionId
*
* @param sessionId
* @return
*/
public static String parseStoreKey(String sessionId) {
if (sessionId!=null && sessionId.indexOf("_")>-1) {
String[] sessionIdArr = sessionId.split("_");
if (sessionIdArr.length==2
&& sessionIdArr[0]!=null
&& sessionIdArr[0].trim().length()>0) {
String userId = sessionIdArr[0].trim();
return userId;
}
}
return null;
}
5.最后通过登录接口返回原先需要认证的系统(客户端),通过客户端的逻辑完成登录流程
其他客户端登录由于cookie和redis中已经有值了,直接通过过滤器登陆成功
因篇幅问题不能全部显示,请点此查看更多更全内容