1. 程式人生 > >單點登錄的簡單實現

單點登錄的簡單實現

quest coo oppo jsp tick 定向 應用 spa rri

實現的原理:

  所有子系統的登錄全部交由一個登錄系統去實現,登錄成功後生成一個ticket,並將ticket和用戶名作為一個鍵值對保存在登錄系統中,同時將用戶名保存在cookie中。然後重定向回到原來的請求路徑,子系統會根據ticket請求登錄系統獲取用戶名,成功後,登錄系統刪掉ticket和用戶名,並將用戶名保存在session中,當session中有用戶名後不會再被過濾重定向到登錄系統。

  當一個子系統登錄成功後,請求其他子系統時,session中並沒有用戶名會重定向到登錄系統,登錄系統會獲取cookie中的用戶名,獲取到用戶名後會生成一個ticket,再次將ticket和用戶名作為一個鍵值對保存在登錄系統中,並再重定向會原來的請求並攜帶上ticket,此時就會根據ticket請求登錄系統獲取用戶名,並存入session。

  登出時只需要刪除cookie,並在子系統做判斷,當session中用戶名與cookie中用戶名不一致時,則認為沒有登錄,要求重新登錄即可。

實現步驟:

  因為需要同時啟動3個項目(不同的端口號),一個登錄系統,兩個子系統,使用tomcat不太方便,這裏使用的是jetty插件。

  在pom.xml文件中進行配置,然後使用maven build啟動即可。

<plugins>

<plugin>

<groupId>org.mortbay.jetty</groupId>

<artifactId>jetty

-maven-plugin</artifactId>

<version>8.1.9.v20130131</version>

<configuration>

<stopKey>stop</stopKey>

<stopPort>6001</stopPort> <!-- 3個項目的端口號都不相同 -->

<webAppConfig>

<contextPath>/client1</contextPath>

</webAppConfig>

<scanIntervalSeconds>4</scanIntervalSeconds>

<connectors>

<connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">

<port>8082</port> <!-- 3個項目的端口號都不相同 -->

<maxIdleTime>60000</maxIdleTime>

</connector>

</connectors>

</configuration>

</plugin>

</plugins>

登錄系統:

登錄效驗:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  String username = req.getParameter("username");
  String password = req.getParameter("password");
  String service = req.getParameter("service");//原來請求的路徑
  /**
  * 此處應該訪問數據庫效驗用戶
  * */
  if ("test".equals(username) && "test".equals(password)) {
  Cookie cookie = new Cookie("sso", username);//效驗通過,將用戶名存入cookie
  cookie.setPath("/"); //設置的cookie路徑
  resp.addCookie(cookie); //寫入客戶端
  long time = System.currentTimeMillis();
  String timeString = username + time; //生成ticket憑證
  JVMCache.TICKET_AND_NAME.put(timeString, username); //存入緩存
  /**
  * 將ticket返回給原請求地址(登錄效驗)
  * */
  if (null != service) {
  StringBuilder url = new StringBuilder();
  url.append(service);
  if (0 <= service.indexOf("?")) {
  url.append("&");
  } else {
  url.append("?");
  }
  url.append("ticket=").append(timeString);
  resp.sendRedirect(url.toString()); //請求原路徑
  } else {
  resp.sendRedirect("/ssoServer/index.jsp"); //請求登錄頁面
  }
  } else {
  resp.sendRedirect("/ssoServer/index.jsp?service=" + service);
  }
}

過濾所有請求

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
  HttpServletRequest request = (HttpServletRequest) servletRequest;
  HttpServletResponse response = (HttpServletResponse) servletResponse;
  String service = request.getParameter("service"); //獲取要訪問的原地址
  String ticket = request.getParameter("ticket"); //獲取憑證
  //獲取cookie中的用戶名
  Cookie[] cookies = request.getCookies();
  String username = "";
  if (null != cookies) {
  for (Cookie cookie : cookies) {
  if ("sso".equals(cookie.getName())) {
    username = cookie.getValue();
    break;
    }
   }
 }
//ticket不為空時,為子系統在效驗是否登錄
if (null == service && null != ticket) {
  filterChain.doFilter(servletRequest, servletResponse);
  return;
}
/**
* 用戶已經登錄
* */
if (null != username && !"".equals(username)) {
  long time = System.currentTimeMillis();
  String timeString = username + time; //生成ticket憑證
  JVMCache.TICKET_AND_NAME.put(timeString, username); //存入緩存
  StringBuilder url = new StringBuilder();
  /**
  * 重新生成token與用戶名的緩存,並執行原請求(登錄效驗)
  * */
  url.append(service);
  if (0 <= service.indexOf("?")) {
    url.append("&");
  } else {
    url.append("?");
  }
  url.append("ticket=").append(timeString);
    response.sendRedirect(url.toString()); //請求原路徑並攜帶上ticket
  } else {
    filterChain.doFilter(servletRequest, servletResponse);
  }
}

返回用戶名

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  /**
  * 通過效驗ticket,返回給子系統用戶名
  * */
  String ticket = req.getParameter("ticket");
  String username = JVMCache.TICKET_AND_NAME.get(ticket);
  JVMCache.TICKET_AND_NAME.remove(ticket);
  //不為空時才返回
  if(null!=username&&!"".equals(username)){
    PrintWriter writer = resp.getWriter();
    writer.write(username);
  }
}

子系統:

/**
* 過濾所有請求,判斷用戶是否登錄
* */
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
  HttpServletRequest request = (HttpServletRequest) servletRequest;
  HttpServletResponse response = (HttpServletResponse) servletResponse;
  HttpSession session = request.getSession();
  String username = (String) session.getAttribute("username"); //獲取session裏的用戶名
  String ticket = request.getParameter("ticket"); //獲取ticket
  String url = URLEncoder.encode(request.getRequestURL().toString(), "UTF-8");
  //獲取cookies裏面的用戶名
  Cookie[] cookies = request.getCookies();
  String tempname = "";
  if (null != cookies) {
    for (Cookie cookie : cookies) {
      if ("sso".equals(cookie.getName())) {
        tempname = cookie.getValue();
        break;
      }
    }
  }
  //用戶名為空說明未登錄
  if (null == username||!tempname.equals(username)) { //cookie和session用戶名不一致說明已經登出
    if (null != ticket && !"".equals(ticket)) {
    //通過ticket向登錄服務器發送請求獲取對應用戶名
    PostMethod postMethod = new PostMethod("http://localhost:8081/ssoServer/ticket");
    postMethod.addParameter("ticket", ticket);
    HttpClient httpClient = new HttpClient();
    try {
      httpClient.executeMethod(postMethod);
      username = postMethod.getResponseBodyAsString();
      postMethod.releaseConnection();
    } catch (Exception e) {
      e.printStackTrace();
    }
  //用戶名不為空說明已經登錄,執行原請求
  if (null != username && !"".equals(username)) {
    session.setAttribute("username", username);
    filterChain.doFilter(request, response);
  } else { //用戶名為空,未登錄,跳轉到登錄頁面
    response.sendRedirect("http://localhost:8081/ssoServer/index.jsp?service=" + url);
  }
} else { //用戶名為空,未登錄,跳轉到登錄頁面
  response.sendRedirect("http://localhost:8081/ssoServer/index.jsp?service=" + url);
}
} else { //已登錄,執行原請求
  filterChain.doFilter(request, response);
}
}

單點登錄的簡單實現